1 Introduction

In this tutorial we walk through a gene-level RNA-seq differential expression analysis using Bioconductor packages. We start from the gene-vs-sample count matrix, and thus assume that the raw reads have already been quality controlled and that the gene expression has been quantified (either using alignment and counting, or by applying an alignment-free quantification tool). We perform exploratory data analysis (EDA) for quality assessment and to explore the relationship between samples, then perform differential gene expression analysis, and visually explore the results.

Bioconductor has many packages supporting analysis of high-throughput sequence data, including RNA-seq. The packages that we will use in this tutorial include core packages maintained by the Bioconductor core team for importing and processing raw sequencing data and loading gene annotations. We will also use contributed packages for statistical analysis and visualization of sequencing data. Through scheduled releases every 6 months, the Bioconductor project ensures that all the packages within a release will work together in harmony (hence the “conductor” metaphor). The packages used in this tutorial are loaded with the library function and can be installed by following the Bioconductor package installation instructions. If you use the results from an R package in published research, you can find the proper citation for the software by typing citation("pkgName"), where you would substitute the name of the package for pkgName. Citing methods papers helps to support and reward the individuals who put time into open source software for genomic data analysis.

Many parts of this tutorial are based on a published RNA-seq workflow available via F1000Research (Love et al. 2015) and as a Bioconductor package.

Along this notebook that you can render yourself - we will show you how during the workshop - you will encounter some small quiz sections to keep up with your progress throughout the entire workflow.

1.1 Setup recommendation

We recommend you use the following setup for an effective learning experience

  • Clone or download the workshop repo, available at https://github.com/imbeimainz/RNAseqFrankfurt2023

  • (Make sure you have R/RStudio/Bioconductor all setup, as expected)

  • Work in RStudio, ideally clicking on the RStudio project file

  • Open up the RNAseq_practical.Rmd file, inside RStudio

  • Execute the code chunks/read the information provided

Have fun learning - together!

1.2 Experimental data

The data used in this workflow comes from an RNA-seq experiment (Alasoo et al. 2018), in which the authors identified shared quantitative trait loci (QTLs) for chromatin accessibility and gene expression in human macrophages exposed to IFNgamma, Salmonella and IFNgamma plus Salmonella. Processed data from a subset of 24 samples from this experiment (six female donors, with four treatments each) is available via the macrophage R/Bioconductor package. In particular, the package contains output from Salmon (Patro et al. 2017), as well as a metadata file. More information about how the raw data was processed is available from the package vignette.

We start by setting the path to the folder containing the quantifications (the output folders from Salmon). Since these are provided with an R package, we will point to the extdata subfolder of the installed package. For a typical analysis of your own data, you would point directly to a folder on your storage system (i.e., not using system.file()).

library(macrophage)
dir <- system.file("extdata", package = "macrophage")
list.files(dir)
# [1] "coldata.csv"                   "errs"                         
# [3] "gencode.v29_salmon_0.12.0"     "gencode.v29.annotation.gtf.gz"
# [5] "PRJEB18997.txt"                "quants"                       
# [7] "supp_table_1.csv"              "supp_table_7.csv"

TODO: open up that directory to “better see what is in it”? Idea: it can give them a sense for the what do I expect to get there?

2 Reading the metadata

First, we will read the metadata for the experiment. The main annotations of interest for this tutorial are condition_name, which represents the treatment of the sample (naive, IFN gamma, Salmonella, IFN gamma+Salmonella) and line_id, which represents the donor ID. The sample identifier is given by the names column, and will be used to match the metadata table to the quantifications.

coldata <- read.csv(file.path(dir, "coldata.csv"))[, c(1, 2, 3, 5)]
dim(coldata)
# [1] 24  4
coldata
#             names sample_id line_id condition_name
# 1  SAMEA103885102    diku_A  diku_1          naive
# 2  SAMEA103885347    diku_B  diku_1           IFNg
# 3  SAMEA103885043    diku_C  diku_1         SL1344
# 4  SAMEA103885392    diku_D  diku_1    IFNg_SL1344
# 5  SAMEA103885182    eiwy_A  eiwy_1          naive
# 6  SAMEA103885136    eiwy_B  eiwy_1           IFNg
# 7  SAMEA103885413    eiwy_C  eiwy_1         SL1344
# 8  SAMEA103884967    eiwy_D  eiwy_1    IFNg_SL1344
# 9  SAMEA103885368    fikt_A  fikt_3          naive
# 10 SAMEA103885218    fikt_B  fikt_3           IFNg
# 11 SAMEA103885319    fikt_C  fikt_3         SL1344
# 12 SAMEA103885004    fikt_D  fikt_3    IFNg_SL1344
# 13 SAMEA103885284    ieki_A  ieki_2          naive
# 14 SAMEA103885059    ieki_B  ieki_2           IFNg
# 15 SAMEA103884898    ieki_C  ieki_2         SL1344
# 16 SAMEA103885157    ieki_D  ieki_2    IFNg_SL1344
# 17 SAMEA103885111    podx_A  podx_1          naive
# 18 SAMEA103884919    podx_B  podx_1           IFNg
# 19 SAMEA103885276    podx_C  podx_1         SL1344
# 20 SAMEA103885021    podx_D  podx_1    IFNg_SL1344
# 21 SAMEA103885262    qaqx_A  qaqx_1          naive
# 22 SAMEA103885228    qaqx_B  qaqx_1           IFNg
# 23 SAMEA103885308    qaqx_C  qaqx_1         SL1344
# 24 SAMEA103884949    qaqx_D  qaqx_1    IFNg_SL1344

In addition to the names column, tximeta, which we will use to read the quantification data, requires that coldata has a column named files, pointing to the Salmon output (the quant.sf file) for the respective samples.

coldata$files <- file.path(dir, "quants", coldata$names, "quant.sf.gz")
head(coldata)
#            names sample_id line_id condition_name
# 1 SAMEA103885102    diku_A  diku_1          naive
# 2 SAMEA103885347    diku_B  diku_1           IFNg
# 3 SAMEA103885043    diku_C  diku_1         SL1344
# 4 SAMEA103885392    diku_D  diku_1    IFNg_SL1344
# 5 SAMEA103885182    eiwy_A  eiwy_1          naive
# 6 SAMEA103885136    eiwy_B  eiwy_1           IFNg
#                                                                                                                        files
# 1 /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/macrophage/extdata/quants/SAMEA103885102/quant.sf.gz
# 2 /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/macrophage/extdata/quants/SAMEA103885347/quant.sf.gz
# 3 /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/macrophage/extdata/quants/SAMEA103885043/quant.sf.gz
# 4 /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/macrophage/extdata/quants/SAMEA103885392/quant.sf.gz
# 5 /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/macrophage/extdata/quants/SAMEA103885182/quant.sf.gz
# 6 /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/macrophage/extdata/quants/SAMEA103885136/quant.sf.gz
all(file.exists(coldata$files))
# [1] TRUE

Now we have everything we need, and can import the quantifications with tximeta. In this process, we will see that tximeta automatically identifies the source and version of the transcriptome reference that was used for the quantification, and adds some metadata. The imported data will be stored in a SummarizedExperiment container.

3 Importing quantifications into R

We will next read the Salmon quantifications provided in the macrophage package into R and summarize the expected counts on the gene level. A simple way to import results from a variety of transcript abundance estimation tools into R is provided by the tximport and tximeta packages. Here, tximport reads the quantifications into a list of matrices, while tximeta instead aggregates the information into a SummarizedExperiment object, and also automatically adds additional annotations for the features. Both packages can return quantifications on the transcript level or aggregate them on the gene level. They also calculate average transcript lengths for each gene and each sample, which can be used as offsets to improve the differential expression analysis by accounting for differential isoform usage across samples (Soneson, Love, and Robinson 2015).

The code below imports the Salmon quantifications into R using the tximeta package. Note how the transcriptome that was used for the quantification is automatically recognized and used to annotate the resulting data object. In order for this to work, tximeta requires that the output folder structure from Salmon is retained, since it reads information from the associated log files in addition to the quantified abundances themselves.

suppressPackageStartupMessages({
    library(tximeta)
    library(DESeq2)
    library(org.Hs.eg.db)
    library(SummarizedExperiment)
})

## Import quantifications on the transcript level
st <- tximeta(coldata = coldata, type = "salmon", dropInfReps = TRUE)

st
# class: RangedSummarizedExperiment 
# dim: 205870 24 
# metadata(6): tximetaInfo quantInfo ... txomeInfo txdbInfo
# assays(3): counts abundance length
# rownames(205870): ENST00000456328.2 ENST00000450305.2 ...
#   ENST00000387460.2 ENST00000387461.2
# rowData names(3): tx_id gene_id tx_name
# colnames(24): SAMEA103885102 SAMEA103885347 ... SAMEA103885308
#   SAMEA103884949
# colData names(4): names sample_id line_id condition_name

We see that tximeta has identified the transcriptome used for the quantification as GENCODE - Homo sapiens - release 29. How did this happen? In fact, the output directory from Salmon contains much more information than just the quant.sf file! (as mentioned above, this means that it is not advisable to move files out of the folder, or to share only the quant.sf file, since the context is lost):

list.files(file.path(dir, "quants", coldata$names[1]), recursive = TRUE)
#  [1] "aux_info/ambig_info.tsv.gz"       "aux_info/bootstrap/bootstraps.gz"
#  [3] "aux_info/bootstrap/names.tsv.gz"  "aux_info/exp_gc.gz"              
#  [5] "aux_info/expected_bias.gz"        "aux_info/fld.gz"                 
#  [7] "aux_info/meta_info.json"          "aux_info/obs_gc.gz"              
#  [9] "aux_info/observed_bias_3p.gz"     "aux_info/observed_bias.gz"       
# [11] "cmd_info.json"                    "lib_format_counts.json"          
# [13] "libParams/flenDist.txt"           "logs/salmon_quant.log.txt"       
# [15] "quant.sf.gz"

In particular, the meta_info.json file contains a hash checksum, which is derived from the set of transcripts used as reference during the quantification and which lets tximeta identify the reference source (by comparing to a table of these hash checksums for commonly used references).

rjson::fromJSON(file = file.path(dir, "quants", coldata$names[1], 
                                 "aux_info", "meta_info.json"))
# $salmon_version
# [1] "0.12.0"
# 
# $samp_type
# [1] "gibbs"
# 
# $opt_type
# [1] "vb"
# 
# $quant_errors
# list()
# 
# $num_libraries
# [1] 1
# 
# $library_types
# [1] "ISR"
# 
# $frag_dist_length
# [1] 1001
# 
# $seq_bias_correct
# [1] FALSE
# 
# $gc_bias_correct
# [1] TRUE
# 
# $num_bias_bins
# [1] 4096
# 
# $mapping_type
# [1] "mapping"
# 
# $num_targets
# [1] 205870
# 
# $serialized_eq_classes
# [1] FALSE
# 
# $eq_class_properties
# list()
# 
# $length_classes
# [1]    520    669   1065   2328 205012
# 
# $index_seq_hash
# [1] "40849ed828ea7d6a94af54a5c40e5d87eb0ce0fc1e9513208a5cffe59d442292"
# 
# $index_name_hash
# [1] "77aca5545a0626421efb4730dd7b95482c77da261f9bdef70d36e25ee68bb7ef"
# 
# $index_seq_hash512
# [1] "f37ae2a7412efd8518d68c22fc3fcc2478b59833809382abd5d9055475505e516730c70914343af34c9232af92fe22832e70afde6b38c26381097068e1e82551"
# 
# $index_name_hash512
# [1] "67f286e5ec2895c10aa6e3a8feed04cb3a80747de7b3afb620298078481b303d2f979407fc104d4f3e8af0e82be2acb2f294ee5f2f68c00087f2855120d9f4ed"
# 
# $num_bootstraps
# [1] 20
# 
# $num_processed
# [1] 45141218
# 
# $num_mapped
# [1] 40075160
# 
# $percent_mapped
# [1] 88.77731
# 
# $call
# [1] "quant"
# 
# $start_time
# [1] "Tue Jan 15 21:21:22 2019"
# 
# $end_time
# [1] "Tue Jan 15 21:29:00 2019"

Looking at the size of the SummarizedExperiment object (205,870 rows!) as well as the row names, we see that this object contains transcript-level information. The assays are created by directly importing the values from the quant.sf files and combining this information across the 24 samples:

  • counts - NumReads column
  • abundance - TPM column
  • length - EffectiveLength column

We can access any of the assays via the assay function:

head(assay(st, "counts"), 3)
#                   SAMEA103885102 SAMEA103885347 SAMEA103885043 SAMEA103885392
# ENST00000456328.2         22.058         12.404           5.44          0.000
# ENST00000450305.2          0.000          0.000           0.00          0.000
# ENST00000488147.1        119.092        180.069         161.55         93.747
#                   SAMEA103885182 SAMEA103885136 SAMEA103885413 SAMEA103884967
# ENST00000456328.2            0.0         10.833          5.119          6.708
# ENST00000450305.2            0.0          0.000          0.000          0.000
# ENST00000488147.1          145.5        141.607        189.152         98.019
#                   SAMEA103885368 SAMEA103885218 SAMEA103885319 SAMEA103885004
# ENST00000456328.2          0.000          4.484          0.000          0.000
# ENST00000450305.2          0.000          0.000          0.000          0.000
# ENST00000488147.1        132.243         88.429         96.871         66.813
#                   SAMEA103885284 SAMEA103885059 SAMEA103884898 SAMEA103885157
# ENST00000456328.2         27.337         12.401          0.000          6.107
# ENST00000450305.2          0.000          0.000          0.000          0.000
# ENST00000488147.1        250.127        211.475        144.212        134.842
#                   SAMEA103885111 SAMEA103884919 SAMEA103885276 SAMEA103885021
# ENST00000456328.2         23.333         11.794          4.670          0.000
# ENST00000450305.2          0.000          0.000          0.000          0.000
# ENST00000488147.1        205.167        151.599        134.082         69.402
#                   SAMEA103885262 SAMEA103885228 SAMEA103885308 SAMEA103884949
# ENST00000456328.2          7.629          3.907          9.195          0.000
# ENST00000450305.2          0.000          0.000          0.000          0.000
# ENST00000488147.1        154.885        125.882        183.322         53.233

You may have noted that st is in fact a RangedSummarizedExperiment object (rather than “just” a SummarizedExperiment object). What does this mean? Let’s look at the information we have about the rows (transcripts) in the object:

rowRanges(st)
# GRanges object with 205870 ranges and 3 metadata columns:
#                     seqnames      ranges strand |     tx_id           gene_id
#                        <Rle>   <IRanges>  <Rle> | <integer>   <CharacterList>
#   ENST00000456328.2     chr1 11869-14409      + |         1 ENSG00000223972.5
#   ENST00000450305.2     chr1 12010-13670      + |         2 ENSG00000223972.5
#   ENST00000488147.1     chr1 14404-29570      - |      9483 ENSG00000227232.5
#   ENST00000619216.1     chr1 17369-17436      - |      9484 ENSG00000278267.1
#   ENST00000473358.1     chr1 29554-31097      + |         3 ENSG00000243485.5
#                 ...      ...         ...    ... .       ...               ...
#   ENST00000361681.2     chrM 14149-14673      - |    206692 ENSG00000198695.2
#   ENST00000387459.1     chrM 14674-14742      - |    206693 ENSG00000210194.1
#   ENST00000361789.2     chrM 14747-15887      + |    206684 ENSG00000198727.2
#   ENST00000387460.2     chrM 15888-15953      + |    206685 ENSG00000210195.2
#   ENST00000387461.2     chrM 15956-16023      - |    206694 ENSG00000210196.2
#                               tx_name
#                           <character>
#   ENST00000456328.2 ENST00000456328.2
#   ENST00000450305.2 ENST00000450305.2
#   ENST00000488147.1 ENST00000488147.1
#   ENST00000619216.1 ENST00000619216.1
#   ENST00000473358.1 ENST00000473358.1
#                 ...               ...
#   ENST00000361681.2 ENST00000361681.2
#   ENST00000387459.1 ENST00000387459.1
#   ENST00000361789.2 ENST00000361789.2
#   ENST00000387460.2 ENST00000387460.2
#   ENST00000387461.2 ENST00000387461.2
#   -------
#   seqinfo: 25 sequences (1 circular) from hg38 genome

By knowing the source and version of the reference used for the quantification, tximeta was able to retrieve the annotation files and decorate the object with information about the transcripts, such as the chromosome and position, and the corresponding gene ID. Importantly, Salmon did not use (or know about) any of this during the quantification! It needs only the transcript sequences. If we just want the annotation columns, without the ranges, we can get those with the rowData accessor:

rowData(st)
# DataFrame with 205870 rows and 3 columns
#                       tx_id           gene_id           tx_name
#                   <integer>   <CharacterList>       <character>
# ENST00000456328.2         1 ENSG00000223972.5 ENST00000456328.2
# ENST00000450305.2         2 ENSG00000223972.5 ENST00000450305.2
# ENST00000488147.1      9483 ENSG00000227232.5 ENST00000488147.1
# ENST00000619216.1      9484 ENSG00000278267.1 ENST00000619216.1
# ENST00000473358.1         3 ENSG00000243485.5 ENST00000473358.1
# ...                     ...               ...               ...
# ENST00000361681.2    206692 ENSG00000198695.2 ENST00000361681.2
# ENST00000387459.1    206693 ENSG00000210194.1 ENST00000387459.1
# ENST00000361789.2    206684 ENSG00000198727.2 ENST00000361789.2
# ENST00000387460.2    206685 ENSG00000210195.2 ENST00000387460.2
# ENST00000387461.2    206694 ENSG00000210196.2 ENST00000387461.2

Similar to the row annotations in rowData, the SummarizedExperiment object contains sample annotations in the colData slot.

colData(st)
# DataFrame with 24 rows and 4 columns
#                         names   sample_id     line_id condition_name
#                   <character> <character> <character>    <character>
# SAMEA103885102 SAMEA103885102      diku_A      diku_1          naive
# SAMEA103885347 SAMEA103885347      diku_B      diku_1           IFNg
# SAMEA103885043 SAMEA103885043      diku_C      diku_1         SL1344
# SAMEA103885392 SAMEA103885392      diku_D      diku_1    IFNg_SL1344
# SAMEA103885182 SAMEA103885182      eiwy_A      eiwy_1          naive
# ...                       ...         ...         ...            ...
# SAMEA103885021 SAMEA103885021      podx_D      podx_1    IFNg_SL1344
# SAMEA103885262 SAMEA103885262      qaqx_A      qaqx_1          naive
# SAMEA103885228 SAMEA103885228      qaqx_B      qaqx_1           IFNg
# SAMEA103885308 SAMEA103885308      qaqx_C      qaqx_1         SL1344
# SAMEA103884949 SAMEA103884949      qaqx_D      qaqx_1    IFNg_SL1344

Quiz

  • How many samples are there in this macrophage dataset? How many are there in your (typical) RNA-seq experiment?
  • Is the quantification done at the transcript level or at the gene level? How can you go from one to the other? And vice versa?
  • What is “better”? Think of DGE, DTU, DTE… and of your experimental setting

4 Summarizing on the gene level

As we saw, the features in the SummarizedExperiment object above are individual transcripts, rather than genes. Often, however, we want to do analysis on the gene level, since the gene-level abundances are more robust and sometimes more interpretable than transcript-level abundances. The rowData contains the information about the corresponding gene for each transcript, in the gene_id column, and tximeta provides a function to summarize on the gene level:

  • Counts are added up
  • TPMs are added up
  • Transcript lengths are added up after weighting by the respective transcript TPMs
## Summarize quantifications on the gene level
sg <- tximeta::summarizeToGene(st)
sg
# class: RangedSummarizedExperiment 
# dim: 58294 24 
# metadata(7): tximetaInfo quantInfo ... txdbInfo assignRanges
# assays(3): counts abundance length
# rownames(58294): ENSG00000000003.14 ENSG00000000005.5 ...
#   ENSG00000285993.1 ENSG00000285994.1
# rowData names(2): gene_id tx_ids
# colnames(24): SAMEA103885102 SAMEA103885347 ... SAMEA103885308
#   SAMEA103884949
# colData names(4): names sample_id line_id condition_name

# compare e.g. to 
st
# class: RangedSummarizedExperiment 
# dim: 205870 24 
# metadata(6): tximetaInfo quantInfo ... txomeInfo txdbInfo
# assays(3): counts abundance length
# rownames(205870): ENST00000456328.2 ENST00000450305.2 ...
#   ENST00000387460.2 ENST00000387461.2
# rowData names(3): tx_id gene_id tx_name
# colnames(24): SAMEA103885102 SAMEA103885347 ... SAMEA103885308
#   SAMEA103884949
# colData names(4): names sample_id line_id condition_name

Now we have a new RangedSummarizedExperiment object, with one row per gene. The row ranges have been summarized as well, and can be used for subsetting and interpretation just as for the transcripts.

At this point, the only information we have about the genes in our data set, apart from their genomic location and the associated transcript IDs, is the Ensembl ID. Often we need additional annotations, such as gene symbols. Bioconductor provides a range of annotation packages:

  • OrgDb packages, providing gene-based annotations for a given organism
  • TxDb and EnsDb packages, providing transcript ranges for a given genome build
  • BSgenome packages, providing the genome sequence for a given genome build

For our purposes here, the appropriate OrgDb package is the most suitable, since it contains gene-centric ID conversion tables. Since this is human data, we will use the org.Hs.eg.db package.

## Add gene symbols
sg <- tximeta::addIds(sg, "SYMBOL", gene = TRUE)
sg
# class: RangedSummarizedExperiment 
# dim: 58294 24 
# metadata(7): tximetaInfo quantInfo ... txdbInfo assignRanges
# assays(3): counts abundance length
# rownames(58294): ENSG00000000003.14 ENSG00000000005.5 ...
#   ENSG00000285993.1 ENSG00000285994.1
# rowData names(3): gene_id tx_ids SYMBOL
# colnames(24): SAMEA103885102 SAMEA103885347 ... SAMEA103885308
#   SAMEA103884949
# colData names(4): names sample_id line_id condition_name
head(rowData(sg))
# DataFrame with 6 rows and 3 columns
#                               gene_id
#                           <character>
# ENSG00000000003.14 ENSG00000000003.14
# ENSG00000000005.5   ENSG00000000005.5
# ENSG00000000419.12 ENSG00000000419.12
# ENSG00000000457.13 ENSG00000000457.13
# ENSG00000000460.16 ENSG00000000460.16
# ENSG00000000938.12 ENSG00000000938.12
#                                                                        tx_ids
#                                                               <CharacterList>
# ENSG00000000003.14  ENST00000612152.4,ENST00000373020.8,ENST00000614008.4,...
# ENSG00000000005.5                         ENST00000373031.4,ENST00000485971.1
# ENSG00000000419.12  ENST00000371588.9,ENST00000466152.5,ENST00000371582.8,...
# ENSG00000000457.13 ENST00000367771.10,ENST00000367770.5,ENST00000367772.8,...
# ENSG00000000460.16  ENST00000498289.5,ENST00000472795.5,ENST00000496973.5,...
# ENSG00000000938.12  ENST00000374005.7,ENST00000399173.5,ENST00000374004.5,...
#                         SYMBOL
#                    <character>
# ENSG00000000003.14      TSPAN6
# ENSG00000000005.5         TNMD
# ENSG00000000419.12        DPM1
# ENSG00000000457.13       SCYL3
# ENSG00000000460.16       FIRRM
# ENSG00000000938.12         FGR

To see a list of the possible columns, use the columns function from the AnnotationDbi package:

AnnotationDbi::columns(org.Hs.eg.db)
#  [1] "ACCNUM"       "ALIAS"        "ENSEMBL"      "ENSEMBLPROT"  "ENSEMBLTRANS"
#  [6] "ENTREZID"     "ENZYME"       "EVIDENCE"     "EVIDENCEALL"  "GENENAME"    
# [11] "GENETYPE"     "GO"           "GOALL"        "IPI"          "MAP"         
# [16] "OMIM"         "ONTOLOGY"     "ONTOLOGYALL"  "PATH"         "PFAM"        
# [21] "PMID"         "PROSITE"      "REFSEQ"       "SYMBOL"       "UCSCKG"      
# [26] "UNIPROT"

We can even add annotations where we expect (and would like to retain) multiple mapping values, e.g., associated GO terms:

sg <- addIds(sg, "GO", multiVals = "list", gene = TRUE)
head(rowData(sg))
# DataFrame with 6 rows and 4 columns
#                               gene_id
#                           <character>
# ENSG00000000003.14 ENSG00000000003.14
# ENSG00000000005.5   ENSG00000000005.5
# ENSG00000000419.12 ENSG00000000419.12
# ENSG00000000457.13 ENSG00000000457.13
# ENSG00000000460.16 ENSG00000000460.16
# ENSG00000000938.12 ENSG00000000938.12
#                                                                        tx_ids
#                                                               <CharacterList>
# ENSG00000000003.14  ENST00000612152.4,ENST00000373020.8,ENST00000614008.4,...
# ENSG00000000005.5                         ENST00000373031.4,ENST00000485971.1
# ENSG00000000419.12  ENST00000371588.9,ENST00000466152.5,ENST00000371582.8,...
# ENSG00000000457.13 ENST00000367771.10,ENST00000367770.5,ENST00000367772.8,...
# ENSG00000000460.16  ENST00000498289.5,ENST00000472795.5,ENST00000496973.5,...
# ENSG00000000938.12  ENST00000374005.7,ENST00000399173.5,ENST00000374004.5,...
#                         SYMBOL                                   GO
#                    <character>                               <list>
# ENSG00000000003.14      TSPAN6 GO:0005515,GO:0016020,GO:0039532,...
# ENSG00000000005.5         TNMD GO:0001886,GO:0001937,GO:0005515,...
# ENSG00000000419.12        DPM1 GO:0004169,GO:0004582,GO:0004582,...
# ENSG00000000457.13       SCYL3 GO:0000139,GO:0005515,GO:0005524,...
# ENSG00000000460.16       FIRRM GO:0000278,GO:0000775,GO:0000776,...
# ENSG00000000938.12         FGR GO:0001784,GO:0001784,GO:0001819,...

Note that Salmon returns estimated or expected counts, which are not necessarily integers. They may need to be rounded before they are passed to count-based statistical methods. To obtain consistent results with different pipelines, we round the estimated counts here (note that in practice, DESeq2 will automatically round the counts, while edgeR will work well also with the non-integer values).

assay(sg, "counts") <- round(assay(sg, "counts"))

Quiz

  • Can you start your DE analysis with normalized values such as TPMs?
  • Where can I store additional information about the samples?
  • Where can I store additional information about the features? (What can your features be?)
  • Can you think of a way to read in the required information if you start (AAAAAAAH) from Excel files?

5 Representing counts for differential expression packages

At this point, we have a gene-level count matrix, contained in our SummarizedExperiment object. This is a branching point where we could use a variety of Bioconductor packages for exploration and differential expression of the count matrix, including edgeR (Robinson, McCarthy, and Smyth 2009), DESeq2 (Love, Huber, and Anders 2014), limma with the voom method (Law et al. 2014), DSS (Wu, Wang, and Wu 2013), EBSeq (Leng et al. 2013) and BaySeq (Hardcastle and Kelly 2010). We will continue using DESeq2 and edgeR.

Bioconductor software packages often define and use a custom class for storing data that makes sure that all the needed data slots are consistently provided and fulfill any requirements. In addition, Bioconductor has general data classes (such as the SummarizedExperiment) that can be used to move data between packages. The DEFormats package can be useful for converting between different classes. The core Bioconductor classes also provide useful functionality: for example, subsetting or reordering the rows or columns of a SummarizedExperiment automatically subsets or reorders the associated rowRanges and colData, which can help to prevent accidental sample swaps that would otherwise lead to spurious results. With SummarizedExperiment this is all taken care of behind the scenes.

Each of the packages we will use for differential expression has a specific class of object used to store the summarization of the RNA-seq experiment and the intermediate quantities that are calculated during the statistical analysis of the data. DESeq2 uses a DESeqDataSet and edgeR uses a DGEList.

5.1 The DESeqDataSet, sample information, and the design formula

In DESeq2, the custom class is called DESeqDataSet. It is built on top of the SummarizedExperiment class, and it is easy to convert SummarizedExperiment objects into DESeqDataSet objects. One of the two main differences compared to a SummarizedExperiment object is that the assay slot can be accessed using the counts accessor function, and the DESeqDataSet class enforces that the values in this matrix are non-negative integers.

A second difference is that the DESeqDataSet has an associated design formula. The experimental design is specified at the beginning of the analysis, as it will inform many of the DESeq2 functions how to treat the samples in the analysis (one exception is the size factor estimation, i.e., the adjustment for differing library sizes, which does not depend on the design formula). The design formula tells which columns in the sample information table (colData) specify the experimental design and how these factors should be used in the analysis.

Let’s remind ourselves of the design of our experiment:

colData(sg)
# DataFrame with 24 rows and 4 columns
#                         names   sample_id     line_id condition_name
#                   <character> <character> <character>    <character>
# SAMEA103885102 SAMEA103885102      diku_A      diku_1          naive
# SAMEA103885347 SAMEA103885347      diku_B      diku_1           IFNg
# SAMEA103885043 SAMEA103885043      diku_C      diku_1         SL1344
# SAMEA103885392 SAMEA103885392      diku_D      diku_1    IFNg_SL1344
# SAMEA103885182 SAMEA103885182      eiwy_A      eiwy_1          naive
# ...                       ...         ...         ...            ...
# SAMEA103885021 SAMEA103885021      podx_D      podx_1    IFNg_SL1344
# SAMEA103885262 SAMEA103885262      qaqx_A      qaqx_1          naive
# SAMEA103885228 SAMEA103885228      qaqx_B      qaqx_1           IFNg
# SAMEA103885308 SAMEA103885308      qaqx_C      qaqx_1         SL1344
# SAMEA103884949 SAMEA103884949      qaqx_D      qaqx_1    IFNg_SL1344

We have samples from four different conditions, and six donors:

table(colData(sg)$condition_name)
# 
#        IFNg IFNg_SL1344       naive      SL1344 
#           6           6           6           6
table(colData(sg)$line_id)
# 
# diku_1 eiwy_1 fikt_3 ieki_2 podx_1 qaqx_1 
#      4      4      4      4      4      4

# possible to use a shortcut
table(sg$line_id)
# 
# diku_1 eiwy_1 fikt_3 ieki_2 podx_1 qaqx_1 
#      4      4      4      4      4      4

We want to find the changes in gene expression that can be associated with the different treatments, but we also want to control for differences between the donors. The design which accomplishes this is obtained by writing ~ line_id + condition_name. By including line_id, terms will be added to the model which account for differences across donors, and by adding condition_name we get terms representing the different treatment effects.

Note: it will be helpful for us if the first level of a factor is the reference level (e.g. control, or untreated samples). The reason is that by specifying this, functions further in the pipeline can be used and will give comparisons such as ‘treatment vs control’, without needing to specify additional arguments.

We can relevel the condition_name factor like so:

colData(sg)$condition_name <- factor(colData(sg)$condition_name)
colData(sg)$condition_name <- relevel(colData(sg)$condition_name, ref = "naive")
colData(sg)$condition_name
#  [1] naive       IFNg        SL1344      IFNg_SL1344 naive       IFNg       
#  [7] SL1344      IFNg_SL1344 naive       IFNg        SL1344      IFNg_SL1344
# [13] naive       IFNg        SL1344      IFNg_SL1344 naive       IFNg       
# [19] SL1344      IFNg_SL1344 naive       IFNg        SL1344      IFNg_SL1344
# Levels: naive IFNg IFNg_SL1344 SL1344

You can use R’s formula notation to express any fixed-effects experimental design for edgeR or DESeq2. Note that these packages use the same formula notation as, for instance, the lm function of base R.

Using the ExploreModelMatrix R/Bioconductor package, we can represent our design in a graphical way:

library(ExploreModelMatrix)
vd <- VisualizeDesign(sampleData = colData(sg), 
                      designFormula = ~ line_id + condition_name,
                      textSizeFitted = 4)
vd$plotlist
# [[1]]

vd$cooccurrenceplots
# [[1]]

We can also open the interactive interface to explore our design further:

ExploreModelMatrix(sampleData = colData(sg), 
                   designFormula = ~ line_id + condition_name)

To generate a DESeqDataSet object from a SummarizedExperiment object, we only need to additionally provide the experimental design in terms of a formula.

dds <- DESeqDataSet(sg, design = ~ line_id + condition_name)

We can also create a DESeqDataSet directly from a count matrix, a data frame with sample information and a design formula (see the DESeqDataSetFromMatrix function).

5.2 The DGEList

As mentioned above, the edgeR package uses another type of data container, namely a DGEList object. tximeta provides a convenient wrapper function to generate a DGEList from the gene-level SummarizedExperiment object:

library(edgeR)

dge <- tximeta::makeDGEList(sg)
names(dge)
# [1] "counts"  "samples" "genes"   "offset"
head(dge$samples)
#                group lib.size norm.factors          names sample_id line_id
# SAMEA103885102     1 40074879            1 SAMEA103885102    diku_A  diku_1
# SAMEA103885347     1 40467661            1 SAMEA103885347    diku_B  diku_1
# SAMEA103885043     1 41832780            1 SAMEA103885043    diku_C  diku_1
# SAMEA103885392     1 42535180            1 SAMEA103885392    diku_D  diku_1
# SAMEA103885182     1 40738502            1 SAMEA103885182    eiwy_A  eiwy_1
# SAMEA103885136     1 39701890            1 SAMEA103885136    eiwy_B  eiwy_1
#                condition_name
# SAMEA103885102          naive
# SAMEA103885347           IFNg
# SAMEA103885043         SL1344
# SAMEA103885392    IFNg_SL1344
# SAMEA103885182          naive
# SAMEA103885136           IFNg

As for the DESeqDataSet, a DGEList can also be generated directly from a count matrix and sample metadata (see the DGEList() constructor function). Just like the SummarizedExperiment and the DESeqDataSet, the DGEList contains all the information we need: the count matrix, information about the samples (the columns of the count matrix), and information about the genes (the rows of the count matrix). One difference compared to the DESeqDataSet is that the experimental design is not defined when creating the DGEList, but later in the workflow.

Quiz

  • How important is the definition of the design?
  • How do I quantify the effect size? “In which direction” is this to be interpreted?
  • Is it possible to change the reference in the comparison?
  • If you have multiple experimental factors, how should you proceed? Think of 2 condition-2 tissues/cell types

6 Filtering

It is often helpful to filter out lowly expressed genes before continuing with the analysis, to remove features that have nearly no information, increase the speed of the analysis and reduce the size of the data. At the very least we exclude genes with zero counts across all samples.

nrow(dds)
# [1] 58294
table(rowSums(assay(dds, "counts")) == 0)
# 
# FALSE  TRUE 
# 38829 19465

Here, we additionally remove genes that have a single read across the samples.

keep <- rowSums(counts(dds)) > 1
dds <- dds[keep, ]
dge <- dge[match(rownames(dds), rownames(dge)), ]
dim(dds)
# [1] 35683    24
dim(dge)
# [1] 35683    24

Importantly, the group information should not be used to define the filtering criterion, since that can interfere with the validity of the p-values downstream.

Quiz

  • Removing unexpressed features might influence what you do when integrating different analyses. How could we proceed in such cases?
  • What if I download publicly available data and some genes are missing? What can be some of the possible reasons for this?

7 Exploratory analysis and visualization

There are two separate analysis paths in this tutorial:

  1. visual exploration of sample relationships, in which we will discuss transformation of the counts for computing distances or making plots
  2. statistical testing for differences attributable to treatment, controlling for donor effects

Importantly, the statistical testing methods rely on original count data (not scaled or transformed) for calculating the precision of measurements. However, for visualization and exploratory analysis, transformed counts are typically more suitable. Thus, it is critical to separate the two workflows and use the appropriate input data for each of them.

7.1 Transformations

Many common statistical methods for exploratory analysis of multidimensional data, for example clustering and principal components analysis (PCA), work best for data that generally has the same range of variance at different ranges of the mean values. When the expected amount of variance is approximately the same across different mean values, the data is said to be homoskedastic. For RNA-seq raw counts, however, the variance grows with the mean. For example, if one performs PCA directly on a matrix of size-factor-normalized read counts, the result typically depends only on the few most strongly expressed genes because they show the largest absolute differences between samples. A simple and often used strategy to avoid this is to take the logarithm of the normalized count values plus a small pseudocount; however, now the genes with the very lowest counts will tend to dominate the results because, due to the strong Poisson noise inherent to small count values, and the fact that the logarithm amplifies differences for the smallest values, these low count genes will show the strongest relative differences between samples.

As a solution, DESeq2 offers transformations for count data that stabilize the variance across the mean: the regularized logarithm (rlog) and the variance stabilizing transformation (VST). These have slightly different implementations, discussed a bit in the DESeq2 paper and in the vignette, but a similar goal of stabilizing the variance across the range of values. Both produce log2-like values for high counts. Here we will use the variance stabilizing transformation implemented with the vst function.

vsd <- DESeq2::vst(dds)

This returns a DESeqTransform object…

class(vsd)
# [1] "DESeqTransform"
# attr(,"package")
# [1] "DESeq2"

…which retains all the column metadata that was attached to the DESeqDataSet:

head(colData(vsd), 3)
# DataFrame with 3 rows and 4 columns
#                         names   sample_id  line_id condition_name
#                   <character> <character> <factor>       <factor>
# SAMEA103885102 SAMEA103885102      diku_A   diku_1         naive 
# SAMEA103885347 SAMEA103885347      diku_B   diku_1         IFNg  
# SAMEA103885043 SAMEA103885043      diku_C   diku_1         SL1344

7.2 PCA plot

One way to visualize sample-to-sample distances is a principal components analysis (PCA). In this ordination method, the data points (here, the samples) are projected onto the 2D plane such that they spread out in the two directions that explain most of the differences (Figure below). The x-axis (the first principal component, or PC1) is the direction that separates the data points the most (i.e., the direction with the largest variance). The y-axis (the second principal component, or PC2) represents the direction with largest variance subject to the constraint that it must be orthogonal to the first direction. The percent of the total variance that is contained in the direction is printed in the axis label. Note that these percentages do not sum to 100%, because there are more dimensions that contain the remaining variance (although each of these remaining dimensions will explain less than the two that we see).

DESeq2::plotPCA(vsd, intgroup = "condition_name")

Additionally, the pcaExplorer package has some functionality on top to explore datasets from the point of view of Principal Components - including also a functional interpretation of it with the pca2go() function.

library(pcaExplorer)
pcaplot(vsd, intgroup = "condition_name", ellipse = TRUE)

7.3 MDS plot

Another way to reduce dimensionality, which is in many ways similar to PCA, is multidimensional scaling (MDS). For MDS, we first have to calculate all pairwise distances between our objects (samples in this case), and then create a (typically) two-dimensional representation where these pre-calculated distances are represented as accurately as possible. This means that depending on how the pairwise sample distances are defined, the two-dimensional plot can be very different, and it is important to choose a distance that is suitable for the type of data at hand.

edgeR contains a function plotMDS, which operates on a DGEList object and generates a two-dimensional MDS representation of the samples. The default distance between two samples can be interpreted as the “typical” log fold change between the two samples, for the genes that are most different between them (by default, the top 500 genes, but this can be modified). We generate an MDS plot from the DGEList object dge, coloring by the treatment and using different plot symbols for different donors.

Note: Since the DGEList was created using the makeDGEList function, the average transcript length offsets have been incorporated in the object and will be used as offsets in downstream analysis. If this is not the case, we need to estimate TMM normalization factors before performing further analysis.

# dge <- edgeR::calcNormFactors(dge)
plotMDS(dge, top = 500, labels = NULL, 
        col = as.numeric(dge$samples$condition_name), 
        cex = 0.5, gene.selection = "common")

Quiz

  • What if I do not see a clear separation in my PCA plot? Is it still ok to proceed?
  • What should I do if I detect/am aware of a batch effect?
  • Raw data vs normalized data vs transformed data: Which one should I use in “all the cases” I could encounter?
  • “I have seen people using tSNE/UMAP in single cell data, why shouldn’t we do the same here?”

8 Differential expression analysis

8.1 Performing differential expression testing with DESeq2

As we have already specified an experimental design when we created the DESeqDataSet, we can run the differential expression pipeline on the raw counts with a single call to the function DESeq. We can also plot the estimated dispersions.

dds <- DESeq2::DESeq(dds)
DESeq2::plotDispEsts(dds)

The DESeq function will print out a message for the various steps it performs. These are described in more detail in the manual page, which can be accessed by typing ?DESeq. Briefly these are: the estimation of size factors (controlling for differences in the sequencing depth of the samples), the estimation of dispersion values for each gene, and fitting a generalized linear model.

A DESeqDataSet is returned that contains all the fitted parameters within it, and the following section describes how to extract out results tables of interest from this object.

Calling the results function without any arguments will extract the estimated log2 fold changes and p values for the last variable in the design formula. If there are more than 2 levels for this variable, results will extract the results table for a comparison of the last level over the first level. This comparison is printed at the top of the output: condition name SL1344 vs naive. Other comparisons can be performed via the contrast argument. For example, we will focus on comparing the IFN gamma treatment to the naive group.

## Default - SL1344 vs naive
res <- DESeq2::results(dds)
head(res)
# log2 fold change (MLE): condition name SL1344 vs naive 
# Wald test p-value: condition name SL1344 vs naive 
# DataFrame with 6 rows and 6 columns
#                     baseMean log2FoldChange     lfcSE      stat      pvalue
#                    <numeric>      <numeric> <numeric> <numeric>   <numeric>
# ENSG00000000003.14   171.782      0.1171248 0.3008327  0.389335 6.97028e-01
# ENSG00000000419.12   967.527      0.0886824 0.0860008  1.031181 3.02456e-01
# ENSG00000000457.13   681.637      0.7109442 0.1973877  3.601766 3.16062e-04
# ENSG00000000460.16   263.282     -1.0347169 0.2179499 -4.747499 2.05947e-06
# ENSG00000000938.12  2646.887      1.6453083 0.2348000  7.007275 2.43005e-12
# ENSG00000000971.15  3045.742      0.7794411 0.4980265  1.565059 1.17569e-01
#                           padj
#                      <numeric>
# ENSG00000000003.14 8.11132e-01
# ENSG00000000419.12 4.55113e-01
# ENSG00000000457.13 1.22290e-03
# ENSG00000000460.16 1.18399e-05
# ENSG00000000938.12 3.14455e-11
# ENSG00000000971.15 2.20491e-01

## We'll instead focus on IFNgamma vs naive
res <- DESeq2::results(dds, contrast = c("condition_name", "IFNg", "naive"))
head(res)
# log2 fold change (MLE): condition_name IFNg vs naive 
# Wald test p-value: condition name IFNg vs naive 
# DataFrame with 6 rows and 6 columns
#                     baseMean log2FoldChange     lfcSE      stat      pvalue
#                    <numeric>      <numeric> <numeric> <numeric>   <numeric>
# ENSG00000000003.14   171.782     -0.2829860 0.3010930 -0.939862 3.47288e-01
# ENSG00000000419.12   967.527      0.0383933 0.0856623  0.448194 6.54013e-01
# ENSG00000000457.13   681.637      1.2838945 0.1966270  6.529593 6.59486e-11
# ENSG00000000460.16   263.282     -1.4725128 0.2183088 -6.745092 1.52930e-11
# ENSG00000000938.12  2646.887      0.6747921 0.2351631  2.869464 4.11168e-03
# ENSG00000000971.15  3045.742      4.9869519 0.4966828 10.040518 1.01142e-23
#                           padj
#                      <numeric>
# ENSG00000000003.14 5.77116e-01
# ENSG00000000419.12 8.25573e-01
# ENSG00000000457.13 1.62287e-09
# ENSG00000000460.16 4.10702e-10
# ENSG00000000938.12 1.89738e-02
# ENSG00000000971.15 1.13504e-21

As res is a DataFrame object, it carries metadata with information on the meaning of the columns:

mcols(res, use.names = TRUE)
# DataFrame with 6 rows and 2 columns
#                        type            description
#                 <character>            <character>
# baseMean       intermediate mean of normalized c..
# log2FoldChange      results log2 fold change (ML..
# lfcSE               results standard error: cond..
# stat                results Wald statistic: cond..
# pvalue              results Wald test p-value: c..
# padj                results   BH adjusted p-values

The first column, baseMean, is a just the average of the normalized count values, dividing by size factors, taken over all samples in the DESeqDataSet. The remaining four columns refer to a specific contrast, namely the comparison of the IFNg level over the naive level for the factor variable condition_name.

The column log2FoldChange is the effect size estimate. It tells us how much the gene’s expression seems to have changed due to infection with IFN gamma in comparison to naive samples. This value is reported on a logarithmic scale to base 2: for example, a log2 fold change of 1.5 means that the gene’s expression is increased by a multiplicative factor of 2^1.5.

Of course, this estimate has an uncertainty associated with it, which is available in the column lfcSE, the standard error estimate for the log2 fold change estimate. We can also express the uncertainty of a particular effect size estimate as the result of a statistical test. The purpose of a test for differential expression is to test whether the data provides sufficient evidence to conclude that this value is really different from zero. DESeq2 performs for each gene a hypothesis test to see whether evidence is sufficient to decide against the null hypothesis that there is zero effect of the treatment on the gene and that the observed difference between treatment and control was merely caused by experimental variability (i.e., the type of variability that you can expect between different samples in the same treatment group). As usual in statistics, the result of this test is reported as a p value, and it is found in the column pvalue. Remember that a p value indicates the probability that an effect as strong as the observed one, or even stronger, would be seen under the situation described by the null hypothesis.

We can also summarize the results with the following line of code, which reports some additional information, that will be covered in later sections.

summary(res)
# 
# out of 35683 with nonzero total read count
# adjusted p-value < 0.1
# LFC > 0 (up)       : 3718, 10%
# LFC < 0 (down)     : 3437, 9.6%
# outliers [1]       : 0, 0%
# low counts [2]     : 12453, 35%
# (mean count < 2)
# [1] see 'cooksCutoff' argument of ?results
# [2] see 'independentFiltering' argument of ?results
hist(res$pvalue)

## Remove the genes that were filtered out in the independent filtering
hist(res$pvalue[!is.na(res$padj)])


## We also add a couple of extra columns that will be useful for the interactive
## visualization later
rowData(dds)$log10Dispersion <- log10(rowData(dds)$dispersion)

restmp <- DataFrame(res)
restmp$log10BaseMean <- log10(restmp$baseMean)
restmp$mlog10PValue <- -log10(restmp$pvalue)
colnames(restmp) <- paste0("DESeq2_IFNg_vs_naive_", colnames(restmp))
rowData(dds) <- cbind(rowData(dds), restmp)

Note that there are many genes with differential expression due to IFN gamma treatment at the FDR level of 10%. There are two ways to be more strict about which set of genes are considered significant:

  • lower the false discovery rate threshold (the threshold on padj in the results table)
  • raise the log2 fold change threshold from 0 using the lfcThreshold argument of results

If we lower the false discovery rate threshold, we should also tell this value to results(), so that the function will use an alternative threshold for the optimal independent filtering step:

res.05 <- results(dds, alpha = 0.05, 
                  contrast = c("condition_name", "IFNg", "naive"))
table(res.05$padj < 0.05)
# 
# FALSE  TRUE 
# 16423  6115

If we want to raise the log2 fold change threshold, so that we test for genes that show more substantial changes due to treatment, we simply supply a value on the log2 scale. For example, by specifying lfcThreshold = 1, we look for genes that show significant effects of treatment on gene counts more than doubling or less than halving, because 2^1 = 2.

resLFC1 <- results(dds, lfcThreshold = 1, 
                   contrast = c("condition_name", "IFNg", "naive"))
summary(resLFC1)
# 
# out of 35683 with nonzero total read count
# adjusted p-value < 0.1
# LFC > 1.00 (up)    : 748, 2.1%
# LFC < -1.00 (down) : 469, 1.3%
# outliers [1]       : 0, 0%
# low counts [2]     : 0, 0%
# (mean count < 0)
# [1] see 'cooksCutoff' argument of ?results
# [2] see 'independentFiltering' argument of ?results
table(resLFC1$padj < 0.1)
# 
# FALSE  TRUE 
# 34466  1217

Sometimes a subset of the p values in res will be NA (“not available”). This is DESeq’s way of reporting that all counts for this gene were zero, and hence no test was applied. In addition, p values can be assigned NA if the gene was excluded from analysis because it contained an extreme count outlier. For more information, see the outlier detection section of the DESeq2 vignette.

With DESeq2, there is also an easy way to plot the (normalized, transformed) counts for specific genes, using the plotCounts function:

plotCounts(dds, gene = "ENSG00000126561.16", intgroup = "condition_name", 
           normalized = TRUE, transform = FALSE)


GeneTonic::gene_plot(dds, gene = "ENSG00000126561.16", intgroup = "condition_name", 
           normalized = TRUE, transform = FALSE)

8.2 Performing differential expression testing with edgeR

Next we will show how to perform differential expression analysis with edgeR. Recall that we have a DGEList dge, containing all the necessary information:

names(dge)
# [1] "counts"  "samples" "genes"   "offset"

We first define a design matrix, using the same formula syntax as for DESeq2 above.

design <- model.matrix(~ line_id + condition_name, data = dge$samples)
head(design)
#                (Intercept) line_ideiwy_1 line_idfikt_3 line_idieki_2
# SAMEA103885102           1             0             0             0
# SAMEA103885347           1             0             0             0
# SAMEA103885043           1             0             0             0
# SAMEA103885392           1             0             0             0
# SAMEA103885182           1             1             0             0
# SAMEA103885136           1             1             0             0
#                line_idpodx_1 line_idqaqx_1 condition_nameIFNg
# SAMEA103885102             0             0                  0
# SAMEA103885347             0             0                  1
# SAMEA103885043             0             0                  0
# SAMEA103885392             0             0                  0
# SAMEA103885182             0             0                  0
# SAMEA103885136             0             0                  1
#                condition_nameIFNg_SL1344 condition_nameSL1344
# SAMEA103885102                         0                    0
# SAMEA103885347                         0                    0
# SAMEA103885043                         0                    1
# SAMEA103885392                         1                    0
# SAMEA103885182                         0                    0
# SAMEA103885136                         0                    0

While DESeq2 performs independent filtering of lowly expressed genes internally, this is done by the user before applying edgeR. Here, we filter out lowly expressed genes using the filterByExpr() function, and then estimate the dispersion for each gene. Note that it is important that we specify the design in the dispersion calculation (it will be used to determine a suitable number of samples to require a gene to be expressed in). Afterwards, we plot the estimated dispersions.

keep <- edgeR::filterByExpr(dge, design)
dge <- dge[keep, ]
dge <- edgeR::estimateDisp(dge, design)
edgeR::plotBCV(dge)

Finally, we fit the generalized linear model and perform the test. In the glmQLFTest function, we indicate which coefficient (which column in the design matrix) that we would like to test for. It is possible to test more general contrasts as well, and the user guide contains many examples on how to do this. The topTags function extracts the top-ranked genes. You can indicate the adjusted p-value cutoff, and/or the number of genes to keep.

fit <- edgeR::glmQLFit(dge, design)
qlf <- edgeR::glmQLFTest(fit, coef = "condition_nameIFNg")
tt.all <- edgeR::topTags(qlf, n = nrow(dge), sort.by = "none") # all genes
hist(tt.all$table$PValue)

tt <- edgeR::topTags(qlf, n = nrow(dge), p.value = 0.1) # genes with adj.p<0.1
tt10 <- edgeR::topTags(qlf) # just the top 10 by default
tt10
# Coefficient:  condition_nameIFNg 
#                               gene_id       tx_ids  SYMBOL           GO
# ENSG00000174944.8   ENSG00000174944.8 ENST0000....  P2RY14 GO:00058....
# ENSG00000111181.12 ENSG00000111181.12 ENST0000.... SLC6A12 GO:00033....
# ENSG00000234518.2   ENSG00000234518.2 ENST0000....    <NA>           NA
# ENSG00000125347.13 ENSG00000125347.13 ENST0000....    IRF1 GO:00007....
# ENSG00000172399.5   ENSG00000172399.5 ENST0000....   MYOZ2 GO:00001....
# ENSG00000137496.17 ENSG00000137496.17 ENST0000....  IL18BP GO:00055....
# ENSG00000204257.14 ENSG00000204257.14 ENST0000.... HLA-DMA GO:00022....
# ENSG00000204252.13 ENSG00000204252.13 ENST0000.... HLA-DOA GO:00022....
# ENSG00000145365.10 ENSG00000145365.10 ENST0000....    TIFA GO:00027....
# ENSG00000162645.12 ENSG00000162645.12 ENST0000....    GBP2 GO:00001....
#                        logFC   logCPM        F       PValue          FDR
# ENSG00000174944.8   9.796713 5.276214 441.8057 6.585808e-18 1.266319e-13
# ENSG00000111181.12  4.700103 4.271224 520.6368 2.581532e-15 2.481885e-11
# ENSG00000234518.2   8.128197 2.799414 286.8341 4.297828e-15 2.754621e-11
# ENSG00000125347.13  5.552512 9.415299 468.6986 6.709759e-15 3.225381e-11
# ENSG00000172399.5  -7.691173 4.536781 319.9830 1.057980e-14 4.068568e-11
# ENSG00000137496.17  4.045575 7.356263 414.6522 2.063736e-14 6.613586e-11
# ENSG00000204257.14  4.061766 5.544983 385.2794 4.043151e-14 1.110596e-10
# ENSG00000204252.13  5.732558 1.912923 298.1753 6.163881e-14 1.426343e-10
# ENSG00000145365.10  5.187753 6.703715 360.5039 7.394894e-14 1.426343e-10
# ENSG00000162645.12  6.663080 9.603736 360.3795 7.418050e-14 1.426343e-10

The columns in the edgeR result data frame are similar to the ones output by DESeq2. edgeR represents the overall expression level on the log-CPM scale rather than on the normalized count scale that DESeq2 uses. The F column contains the test statistic, and the FDR column contains the Benjamini-Hochberg adjusted p-values.

We can compare the sets of significantly differentially expressed genes to see how the results from the two packages overlap:

shared <- intersect(rownames(res), rownames(tt.all$table))
table(DESeq2 = res$padj[match(shared, rownames(res))] < 0.1, 
      edgeR = tt.all$table$FDR[match(shared, rownames(tt.all$table))] < 0.1)
#        edgeR
# DESeq2  FALSE  TRUE
#   FALSE 11908   365
#   TRUE    319  6635

We can also compare the two result lists by the ranks:

plot(rank(res$pvalue[match(shared, rownames(res))]), 
     rank(tt.all$table$PValue[match(shared, rownames(tt.all$table))]), 
     cex = 0.1, xlab = "DESeq2", ylab = "edgeR")

Also with edgeR we can test for significance relative to a fold-change threshold, using the function glmTreat. Below we set the log fold-change threshold to 1 (i.e., fold change threshold equal to 2), as for DESeq2 above.

treatres <- edgeR::glmTreat(fit, coef = "condition_nameIFNg", lfc = 1)
tt.treat <- edgeR::topTags(treatres, n = nrow(dge), sort.by = "none")

Quiz

  • A feature being called DE: “Why is my expected shortlisted gene not showing up?” What possible factors can play a role in the feature being above or below the significance threshold?
  • Shall I subset my DE results to the genes only detected as DE? Why?
  • The results between DESeq2 and edgeR might differ. Which one is “correct”? How can you better appreciate the existing commonalities and differences in the DE analysis?
  • The thought above is valid also for the process of integrating different DE analyses (i.e. using the same method, but “comparing different groups”). What can you think of to represent these results?

9 Plotting results

9.1 MA plot with DESeq2

An MA-plot (Dudoit et al. 2002) provides a useful overview for an experiment with a two-group comparison. The log2 fold change for a particular comparison is plotted on the y-axis and the average of the counts normalized by size factor is shown on the x-axis (“M” for minus, because a log ratio is equal to log minus log, and “A” for average). Each gene is represented with a dot. Genes with an adjusted p value below a threshold (here 0.1, the default with DESeq2) are shown in color

DESeq2::plotMA(res, ylim = c(-5, 5))

We see that there are many genes with low expression levels that nevertheless have large fold changes (since we are, effectively, dividing by a small number). To get more interpretable log fold changes (e.g., for ranking genes), we use the lfcShrink function to shrink the log2 fold changes for the comparison of IFN gamma-treated vs naive samples. There are three types of shrinkage estimators in DESeq2, which are covered in the vignette. Here we specify the apeglm method for shrinking coefficients, which is good for shrinking the noisy LFC estimates while giving low bias LFC estimates for true large differences (Zhu, Ibrahim, and Love 2019). To use apeglm we specify a coefficient from the model to shrink, either by name or number as the coefficient appears in resultsNames(dds).

library(apeglm)
DESeq2::resultsNames(dds)
#  [1] "Intercept"                           "line_id_eiwy_1_vs_diku_1"           
#  [3] "line_id_fikt_3_vs_diku_1"            "line_id_ieki_2_vs_diku_1"           
#  [5] "line_id_podx_1_vs_diku_1"            "line_id_qaqx_1_vs_diku_1"           
#  [7] "condition_name_IFNg_vs_naive"        "condition_name_IFNg_SL1344_vs_naive"
#  [9] "condition_name_SL1344_vs_naive"      "DESeq2_IFNg_vs_naive_log2FoldChange"
resape <- DESeq2::lfcShrink(dds, coef = "condition_name_IFNg_vs_naive", type = "apeglm")
DESeq2::plotMA(resape, ylim = c(-5, 5))

9.2 MA / Smear plot with edgeR

In edgeR, the MA plot is obtained via the plotSmear function.

edgeR::plotSmear(qlf, de.tags = rownames(tt$table))

9.3 Heatmap of the most significant genes

Another way of representing the results of a differential expression analysis is to construct a heatmap of the top differentially expressed genes. A heatmap is a “color coded expression matrix”, where the rows and columns are clustered using hierarchical clustering. Typically, it should not be applied to counts, but works better with transformed values. Here we show how it can be applied to the variance-stabilized values generated above. We would expect the contrasted sample groups to cluster separately (“by construction”, since the genes were selected to be most discriminative between the groups). The heatmap will allow us to display, e.g., the variability within the groups of the differentially expressed genes. We choose the top 30 differentially expressed genes. There are many functions in R that can generate heatmaps, here we show the one from the pheatmap package.

library(pheatmap)
stopifnot(rownames(vsd) == rownames(res))
mat <- assay(vsd)
rownames(mat) <- rowData(vsd)$SYMBOL
mat <- mat[head(order(res$padj), 30), ]
mat <- mat - rowMeans(mat)
df <- as.data.frame(colData(vsd)[, c("condition_name"), drop = FALSE])
pheatmap(mat, annotation_col = df)

We can of course also create heatmaps for other sets of genes - for example, the collection of genes with the highest overall variance (which may or may not indicate a difference between the groups - in this particular case most of the highly variable genes show a clear difference between the groups).

mat <- assay(vsd)
rownames(mat) <- rowData(vsd)$SYMBOL

topVarGenes <- head(order(rowVars(mat), decreasing = TRUE), 30)

mat <- mat[topVarGenes, ]
mat <- mat - rowMeans(mat)
df <- as.data.frame(colData(vsd)[, c("condition_name"), drop = FALSE])
pheatmap(mat, annotation_col = df)

TODO: see the “stripes” in some genes over all the samples

9.4 Interactive visualization with iSEE

iSEE is a Bioconductor package that allows interactive exploration of any data stored in a SummarizedExperiment container, or any class extending this (such as, e.g., the DESeqDataSet class, or the SingleCellExperiment for single-cell data). By calling the iSEE() function with the object as the first argument, an interactive application will be opened, in which all observed values as well as metadata columns (rowData and colData) can be explored.

library(iSEE)
library(iSEEu)
dds <- iSEEu::registerAveAbPatterns(dds, "log10BaseMean")
dds <- iSEEu::registerLogFCPatterns(dds, "log2FoldChange")
dds <- iSEEu::registerPValuePatterns(dds, "pvalue")
app <- iSEE(dds, initial = list(MAPlot(), VolcanoPlot(), 
                                RowDataTable(), FeatureAssayPlot()))
## shiny::runApp(app)

9.5 Exporting results to CSV file

You can easily save the results table in a CSV file that you can then share or load with a spreadsheet program such as Excel (note, however, that Excel sometimes does funny things to gene identifiers (Zeeberg et al. 2004; Ziemann, Eren, and El-Osta 2016)). The call to as.data.frame is necessary to convert the DataFrame object to a data.frame object that can be processed by write.csv. Here, we first show how to add gene symbols to the output table, and then export just the top 100 genes for demonstration.

stopifnot(all(rownames(res) == rownames(dds)))
res$symbol <- rowData(dds)$SYMBOL

resOrdered <- res[order(res$padj), ]
head(resOrdered)
# log2 fold change (MLE): condition_name IFNg vs naive 
# Wald test p-value: condition name IFNg vs naive 
# DataFrame with 6 rows and 7 columns
#                     baseMean log2FoldChange     lfcSE      stat       pvalue
#                    <numeric>      <numeric> <numeric> <numeric>    <numeric>
# ENSG00000125347.13 30487.254        5.55915  0.218390   25.4551 6.19761e-143
# ENSG00000111181.12   687.519        4.70999  0.195911   24.0415 1.02514e-127
# ENSG00000162645.12 36639.987        6.66498  0.286603   23.2551 1.26442e-119
# ENSG00000137496.17  7118.885        4.05787  0.177049   22.9195 2.97302e-116
# ENSG00000145365.10  3642.657        5.19246  0.237141   21.8961 2.82829e-106
# ENSG00000204257.14  1906.261        4.07091  0.190575   21.3612 3.06785e-101
#                            padj      symbol
#                       <numeric> <character>
# ENSG00000125347.13 1.43971e-138        IRF1
# ENSG00000111181.12 1.19070e-123     SLC6A12
# ENSG00000162645.12 9.79081e-116        GBP2
# ENSG00000137496.17 1.72658e-112      IL18BP
# ENSG00000145365.10 1.31402e-102        TIFA
# ENSG00000204257.14  1.18777e-97     HLA-DMA

resOrderedDF <- as.data.frame(resOrdered)[seq_len(100), ]
write.table(cbind(id = rownames(resOrderedDF), resOrderedDF), 
            file = "results.txt", quote = FALSE, sep = "\t",
            row.names = FALSE)

Quiz

  • Is visualization of data and results really that important?
  • Volcano plot or MA plot, what is “better”? Think of the information every plot type displays
  • Can I always plot expression values for a single gene, or for a set of genes (i.e. independently of their DE status)?
  • What can I do if I want to know more on one “novel” gene, possibly relevant with my experiment?

10 Functional analysis

In order to interpret the differential expression analysis results in terms of known gene sets, we can also apply a functional enrichment test. There are many alternatives to perform enrichment analysis in the context of R and Bioconductor. Among these, topGO and clusterProfiler are two popular options.

We can perform the analysis by following the instructions in the next chunk. In each case, the gene sets that are tested for enrichment are obtained from the Gene Ontology ‘biological process’ catalog.

library(GeneTonic)

res$symbol <- rowData(dds)$SYMBOL

de_symbols_IFNg_vs_naive <- deseqresult2df(res, FDR = 0.05)$symbol
bg_ids <- rowData(dds)$SYMBOL[rowSums(counts(dds)) > 0]

library(topGO)
topgo_DE_macrophage_IFNg_vs_naive <- pcaExplorer::topGOtable(
    DEgenes = de_symbols_IFNg_vs_naive,
    BGgenes = bg_ids,
    ontology = "BP",
    mapping = "org.Hs.eg.db",
    geneID = "symbol",
    topTablerows = 500
)

library(clusterProfiler)
clupro_DE_macrophage_IFNg_vs_naive <- clusterProfiler::enrichGO(
    gene = de_symbols_IFNg_vs_naive,
    universe = bg_ids,
    keyType = "SYMBOL",
    OrgDb = org.Hs.eg.db,
    ont = "BP",
    pAdjustMethod = "BH",
    pvalueCutoff = 0.01,
    qvalueCutoff = 0.05,
    readable = FALSE
)
DT::datatable(topgo_DE_macrophage_IFNg_vs_naive[1:10,])
DT::datatable(as.data.frame(clupro_DE_macrophage_IFNg_vs_naive)[1:10,])

10.1 Streamlining interpretation of results with GeneTonic

“Since it is bioinformatics”, every software package can be expected to return a (slightly) different-but-similar-in-content output format. To simplify the interpretation of transcriptome datasets, the GeneTonic package offers an interactive application to explore in depth all the workflow results.

As a first step, we convert the output of each tool to a consolidated “standard” format, as expected by GeneTonic - this is the first step to construct a GeneTonicList object, as a single container to perform all operations on afterwards, be it in the app or offline by using its functionality in scripts/notebooks.

res_enrich_topGO <- shake_topGOtableResult(topgo_DE_macrophage_IFNg_vs_naive)
res_enrich_clupro <- shake_enrichResult(clupro_DE_macrophage_IFNg_vs_naive)

gtl_macrophage <- GeneTonicList(
    dds = dds,
    res_de = res,
    res_enrich = res_enrich_clupro,
    annotation_obj = data.frame(
        gene_id = rowData(dds)$gene_id,
        gene_name = rowData(dds)$SYMBOL
    )
)

describe_gtl(gtl_macrophage)
#  [1] "---------------------------------\n"                                                      
#  [2] "----- GeneTonicList object ------\n"                                                      
#  [3] "---------------------------------\n"                                                      
#  [4] "\n----- dds object -----\n"                                                               
#  [5] "Providing an expression object (as DESeqDataset) of 35683 features over 24 samples\n"     
#  [6] "\n----- res_de object -----\n"                                                            
#  [7] "Providing a DE result object (as DESeqResults), 35683 features tested, 6072 found as DE\n"
#  [8] "Upregulated:     3224\n"                                                                  
#  [9] "Downregulated:   2848\n"                                                                  
# [10] "\n----- res_enrich object -----\n"                                                        
# [11] "Providing an enrichment result object, 6152 reported\n"                                   
# [12] "\n----- annotation_obj object -----\n"                                                    
# [13] "Providing an annotation object of 35683 features with information on 2 identifier types\n"

## we can store this object as serialized file to load/share/...
saveRDS(gtl_macrophage, "gtl_macrophage.RDS")

After that, we would simply have to call the GeneTonic() function specifying the gtl parameter - this can also be passed at runtime

## and that is it!
GeneTonic(gtl = gtl_macrophage)

## or if expecting to upload at runtime... (e.g. used as a server-like app)
GeneTonic()

Quiz

  • Genes or geneset: What should I use as a key to interpretation?
  • What is the main advantage of using standardized objects vs a bunch of tables?
  • “Ok, I would still like your Bioinformatics group on board for my project. What should I expect to happen?”

11 Bonus: Differential transcript expression with swish

Next, we perform a differential transcript expression analysis with swish. Note that, as opposed to the workflow above, we will make use of the transcript-level abundance estimates. In addition, we need the inferential replicates. Since we ignored these when importing the data above, we will re-import it here.

head(coldata)
st <- tximeta(coldata = coldata, type = "salmon", dropInfReps = FALSE)

We can check that the inferential replicates were imported as well:

assayNames(st)

Since we are interested in comparing the naive and IFNg groups, we subset our object to these groups.

st <- st[, st$condition_name %in% c("naive", "IFNg")]
st$condition_name <- factor(st$condition_name, c("naive", "IFNg"))

Next, we run the DTE analysis with swish. First we’ll scale the inferential replicates, followed by labeling the rows with sufficient counts for running differential expression, and then calculating the statistics.

library(fishpond)
st <- scaleInfReps(st, lengthCorrect = TRUE)
st <- labelKeep(st)
st <- st[mcols(st)$keep, ]
set.seed(1)
st <- swish(st, x = "condition_name", pair = "line_id", nperms = 100)

The results are stored in mcols(st).

head(mcols(st))
table(mcols(st)$qvalue < 0.05)
## Most significant transcripts
tophits <- mcols(st)[order(mcols(st)$qvalue, -abs(mcols(st)$log2FC)), ]
head(tophits)
hist(mcols(st)$pvalue, col = "grey")

We can plot the results for some of the most significant transcripts.

plotInfReps(st, idx = rownames(tophits)[1],
            x = "condition_name", cov = "line_id")
plotInfReps(st, idx = rownames(tophits)[43],
            x = "condition_name", cov = "line_id")
plotInfReps(st, idx = rownames(tophits)[60],
            x = "condition_name", cov = "line_id")
plotMASwish(st, alpha = 0.05)

We can also use the plotting functions of swish to plot the inferential replicates for the top-ranked genes in the differential gene expression analysis.

## Summarize on the gene level
sg_inf <- summarizeToGene(st)
sg_inf <- sg_inf[, sg_inf$condition_name %in% c("naive", "IFNg")]
sg_inf$condition_name <- factor(sg_inf$condition_name, c("naive", "IFNg"))
plotInfReps(sg_inf, idx = rownames(resOrdered)[1],
            x = "condition_name", cov = "line_id")

12 Bonus: Differential transcript usage

Finally, we show how to run differential transcript usage analysis with swish and DEXSeq. With swish, we can build upon the data imported earlier to calculate isoform proportions and perform a permutation test based on these.

iso <- isoformProportions(st)
iso <- swish(iso, x = "condition_name", pair = "line_id", 
             nperms = 100)

For DEXSeq, we will again re-import the data, since we would like the transcript counts to represent so called ‘scaled TPM’ values (similarly to what swish will do internally when scaling the inferential replicates, to avoid differences in transcript length being interpreted as differences in relative abundance between groups). tximeta can do this for us, effectively populating the ‘counts’ assay with TPMs, scaled up to the observed library size to be comparable in size to the actual counts. For DEXSeq, we further subset the transcripts to those on chromosome 18, for computational time reasons.

head(coldata)
st <- tximeta(coldata = coldata, type = "salmon", 
              countsFromAbundance = "scaledTPM")
st <- st[, st$condition_name %in% c("naive", "IFNg")]
st$condition_name <- factor(st$condition_name, c("naive", "IFNg"))
st$sample_id <- colnames(st)
st <- st[seqnames(rowRanges(st)) == "chr18", ]

library(DEXSeq)
dxd <- DEXSeqDataSet(countData = round(assay(st, "counts")),
                     sampleData = as.data.frame(colData(st)),
                     design = ~sample + exon + condition_name:exon,
                     featureID = rowData(st)$tx_name,
                     groupID = as.character(rowData(st)$gene_id))
dxd <- estimateSizeFactors(dxd)
dxd <- estimateDispersions(dxd, quiet = TRUE)
dxd <- testForDEU(dxd, reducedModel = ~sample + exon)
dxr <- DEXSeqResults(dxd, independentFiltering = FALSE)
qval <- perGeneQValue(dxr)
dxr.g <- data.frame(gene = names(qval), qval)
head(dxr)

Finally, we compare the p-value ranks for the genes shared by the two analyses.

shared <- intersect(dxr$featureID, rownames(mcols(iso)))
plot(rank(dxr[match(shared, dxr$featureID), "pvalue"]),
     rank(mcols(iso)[shared, "pvalue"]), cex = 0.1,
     xlab = "DEXSeq", ylab = "swish")

Session information

It is good practice to always include a list of the software versions that were used to perform a given analysis, for reproducibility and trouble-shooting purposes. One way of achieving this is via the sessionInfo() function.

sessionInfo()
# R version 4.4.0 (2024-04-24)
# Platform: x86_64-apple-darwin20
# Running under: macOS Monterey 12.7.1
# 
# Matrix products: default
# BLAS:   /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRblas.0.dylib 
# LAPACK: /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0
# 
# locale:
# [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
# 
# time zone: Europe/Berlin
# tzcode source: internal
# 
# attached base packages:
# [1] stats4    stats     graphics  grDevices utils     datasets  methods  
# [8] base     
# 
# other attached packages:
#  [1] clusterProfiler_4.12.0      topGO_2.56.0               
#  [3] SparseM_1.83                GO.db_3.19.1               
#  [5] graph_1.82.0                GeneTonic_2.8.0            
#  [7] iSEEu_1.16.0                iSEEhex_1.6.0              
#  [9] iSEE_2.16.0                 SingleCellExperiment_1.26.0
# [11] pheatmap_1.0.12             apeglm_1.26.1              
# [13] pcaExplorer_2.30.0          bigmemory_4.6.4            
# [15] edgeR_4.2.0                 limma_3.60.3               
# [17] ExploreModelMatrix_1.16.0   GenomicFeatures_1.57.0     
# [19] org.Hs.eg.db_3.19.1         AnnotationDbi_1.66.0       
# [21] DESeq2_1.44.0               SummarizedExperiment_1.34.0
# [23] Biobase_2.64.0              MatrixGenerics_1.16.0      
# [25] matrixStats_1.3.0           GenomicRanges_1.56.1       
# [27] GenomeInfoDb_1.40.1         IRanges_2.38.0             
# [29] S4Vectors_0.42.0            BiocGenerics_0.50.0        
# [31] tximeta_1.22.1              macrophage_1.20.0          
# [33] rmarkdown_2.27              knitr_1.47                 
# [35] BiocStyle_2.32.1           
# 
# loaded via a namespace (and not attached):
#   [1] GSEABase_1.66.0          vroom_1.6.5              progress_1.2.3          
#   [4] DT_0.33                  Biostrings_2.72.1        vctrs_0.6.5             
#   [7] digest_0.6.35            png_0.1-8                shape_1.4.6.1           
#  [10] shinyBS_0.61.1           registry_0.5-1           ggrepel_0.9.5           
#  [13] MASS_7.3-61              reshape2_1.4.4           httpuv_1.6.15           
#  [16] foreach_1.5.2            qvalue_2.36.0            withr_3.0.0             
#  [19] ggfun_0.1.5              xfun_0.45                survival_3.7-0          
#  [22] memoise_2.0.1            hexbin_1.28.3            gson_0.1.0              
#  [25] tidytree_0.4.6           GlobalOptions_0.1.2      prettyunits_1.2.0       
#  [28] KEGGREST_1.44.1          promises_1.3.0           httr_1.4.7              
#  [31] restfulr_0.0.15          rstudioapi_0.16.0        shinyAce_0.4.2          
#  [34] UCSC.utils_1.0.0         miniUI_0.1.1.1           generics_0.1.3          
#  [37] DOSE_3.30.1              archive_1.1.8            base64enc_0.1-3         
#  [40] curl_5.2.1               zlibbioc_1.50.0          ggraph_2.2.1            
#  [43] polyclip_1.10-6          ca_0.71.1                GenomeInfoDbData_1.2.12 
#  [46] SparseArray_1.4.8        RBGL_1.80.0              threejs_0.3.3           
#  [49] xtable_1.8-4             stringr_1.5.1            doParallel_1.0.17       
#  [52] evaluate_0.24.0          S4Arrays_1.4.1           BiocFileCache_2.12.0    
#  [55] hms_1.1.3                bookdown_0.39            colorspace_2.1-0        
#  [58] filelock_1.0.3           visNetwork_2.1.2         shinyWidgets_0.8.6      
#  [61] magrittr_2.0.3           readr_2.1.5              Rgraphviz_2.48.0        
#  [64] ggtree_3.12.0            later_1.3.2              viridis_0.6.5           
#  [67] lattice_0.22-6           NMF_0.27                 genefilter_1.86.0       
#  [70] shadowtext_0.1.3         XML_3.99-0.16.1          cowplot_1.1.3           
#  [73] pillar_1.9.0             nlme_3.1-165             iterators_1.0.14        
#  [76] gridBase_0.4-7           compiler_4.4.0           stringi_1.8.4           
#  [79] shinycssloaders_1.0.0    Category_2.70.0          TSP_1.2-4               
#  [82] dendextend_1.17.1        GenomicAlignments_1.40.0 plyr_1.8.9              
#  [85] crayon_1.5.3             abind_1.4-5              BiocIO_1.14.0           
#  [88] gridGraphics_0.5-1       emdbook_1.3.13           locfit_1.5-9.9          
#  [91] graphlayouts_1.1.1       bit_4.0.5                dplyr_1.1.4             
#  [94] fastmatch_1.1-4          codetools_0.2-20         crosstalk_1.2.1         
#  [97] bslib_0.7.0              GetoptLong_1.0.5         plotly_4.10.4           
# [100] mime_0.12                splines_4.4.0            circlize_0.4.16         
# [103] Rcpp_1.0.12              dbplyr_2.5.0             tippy_0.1.0             
# [106] HDO.db_0.99.1            blob_1.2.4               utf8_1.2.4              
# [109] clue_0.3-65              BiocVersion_3.19.1       AnnotationFilter_1.28.0 
# [112] fs_1.6.4                 backbone_2.1.4           expm_0.999-9            
# [115] ggplotify_0.1.2          tibble_3.2.1             Matrix_1.7-0            
# [118] statmod_1.5.0            tzdb_0.4.0               tweenr_2.0.3            
# [121] pkgconfig_2.0.3          tools_4.4.0              cachem_1.1.0            
# [124] RSQLite_2.3.7            viridisLite_0.4.2        DBI_1.2.3               
# [127] numDeriv_2016.8-1.1      fastmap_1.2.0            scales_1.3.0            
# [130] grid_4.4.0               shinydashboard_0.7.2     Rsamtools_2.20.0        
# [133] AnnotationHub_3.12.0     sass_0.4.9               patchwork_1.2.0         
# [136] coda_0.19-4.1            BiocManager_1.30.23      fontawesome_0.5.2       
# [139] farver_2.1.2             scatterpie_0.2.3         tidygraph_1.3.1         
# [142] mgcv_1.9-1               yaml_2.3.8               AnnotationForge_1.46.0  
# [145] rtracklayer_1.64.0       cli_3.6.2                purrr_1.0.2             
# [148] txdbmaker_1.0.0          webshot_0.5.5            lifecycle_1.0.4         
# [151] mvtnorm_1.2-5            tximport_1.32.0          rintrojs_0.3.4          
# [154] BiocParallel_1.38.0      annotate_1.82.0          gtable_0.3.5            
# [157] rjson_0.2.21             ggridges_0.5.6           ape_5.8                 
# [160] parallel_4.4.0           jsonlite_1.8.8           colourpicker_1.3.0      
# [163] seriation_1.5.5          bitops_1.0-7             ggplot2_3.5.1           
# [166] bigmemory.sri_0.1.8      bit64_4.0.5              assertthat_0.2.1        
# [169] yulab.utils_0.1.4        heatmaply_1.5.0          bs4Dash_2.3.3           
# [172] bdsmatrix_1.3-7          icons_0.2.0              jquerylib_0.1.4         
# [175] highr_0.11               GOSemSim_2.30.0          shinyjs_2.1.0           
# [178] lazyeval_0.2.2           shiny_1.8.1.1            dynamicTreeCut_1.63-1   
# [181] htmltools_0.5.8.1        enrichplot_1.24.0        rappdirs_0.3.3          
# [184] ensembldb_2.28.0         glue_1.7.0               httr2_1.0.1             
# [187] XVector_0.44.0           RCurl_1.98-1.14          treeio_1.28.0           
# [190] ComplexUpset_1.3.3       gridExtra_2.3            igraph_2.0.3            
# [193] R6_2.5.1                 tidyr_1.3.1              labeling_0.4.3          
# [196] listviewer_4.0.0         cluster_2.1.6            rngtools_1.5.2          
# [199] bbmle_1.0.25.1           aplot_0.2.3              DelayedArray_0.30.1     
# [202] tidyselect_1.2.1         vipor_0.4.7              ProtGenerics_1.36.0     
# [205] GOstats_2.70.0           ggforce_0.4.2            xml2_1.3.6              
# [208] munsell_0.5.1            data.table_1.15.4        htmlwidgets_1.6.4       
# [211] fgsea_1.30.0             ComplexHeatmap_2.20.0    RColorBrewer_1.1-3      
# [214] biomaRt_2.60.0           rlang_1.1.4              uuid_1.2-0              
# [217] fansi_1.0.6

References

Alasoo, Kaur, Julia Rodrigues, Subhankar Mukhopadhyay, Andrew J Knights, Alice L Mann, Kousik Kundu, HIPSCI Consortium, Christine Hale, Gordon Dougan, and Daniel J Gaffney. 2018. “Shared Genetic Effects on Chromatin and Gene Expression Indicate a Role for Enhancer Priming in Immune Response.” Nat. Genet. 50 (3): 424–31.
Dudoit, Sandrine, Yee H. Yang, Matthew J. Callow, and Terence P. Speed. 2002. Statistical methods for identifying differentially expressed genes in replicated cDNA microarray experiments.” In Statistica Sinica, 111–39. http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.117.9702.
Hardcastle, Thomas, and Krystyna Kelly. 2010. baySeq: Empirical Bayesian methods for identifying differential expression in sequence count data.” BMC Bioinformatics 11 (1): 422+. https://doi.org/10.1186/1471-2105-11-422.
Law, Charity W., Yunshun Chen, Wei Shi, and Gordon K. Smyth. 2014. Voom: precision weights unlock linear model analysis tools for RNA-seq read counts.” Genome Biology 15 (2): R29+. https://doi.org/10.1186/gb-2014-15-2-r29.
Leng, N., J. A. Dawson, J. A. Thomson, V. Ruotti, A. I. Rissman, B. M. G. Smits, J. D. Haag, M. N. Gould, R. M. Stewart, and C. Kendziorski. 2013. EBSeq: an empirical Bayes hierarchical model for inference in RNA-seq experiments.” Bioinformatics 29 (8): 1035–43. https://doi.org/10.1093/bioinformatics/btt087.
Love, Michael I., Simon Anders, Vladislav Kim, and Wolfgang Huber. 2015. RNA-Seq Workflow: Gene-Level Exploratory Analysis and Differential Expression.” F1000Research, October. https://doi.org/10.12688/f1000research.7035.1.
Love, Michael I., Wolfgang Huber, and Simon Anders. 2014. Moderated estimation of fold change and dispersion for RNA-seq data with DESeq2.” Genome Biology 15 (12): 550+. https://doi.org/10.1186/s13059-014-0550-8.
Patro, Rob, Geet Duggal, Michael I Love, Rafael A Irizarry, and Carl Kingsford. 2017. “Salmon Provides Fast and Bias-Aware Quantification of Transcript Expression.” Nat. Methods.
Robinson, M. D., D. J. McCarthy, and G. K. Smyth. 2009. edgeR: a Bioconductor package for differential expression analysis of digital gene expression data.” Bioinformatics 26 (1): 139–40. https://doi.org/10.1093/bioinformatics/btp616.
Soneson, Charlotte, Michael I. Love, and Mark Robinson. 2015. Differential analyses for RNA-seq: transcript-level estimates improve gene-level inferences.” F1000Research 4. https://doi.org/10.12688/f1000research.7563.1.
Wu, Hao, Chi Wang, and Zhijin Wu. 2013. A new shrinkage estimator for dispersion improves differential expression detection in RNA-seq data.” Biostatistics 14 (2): 232–43. https://doi.org/10.1093/biostatistics/kxs033.
Zeeberg, Barry R, Joseph Riss, David W Kane, Kimberly J Bussey, Edward Uchio, W Marston Linehan, J Carl Barrett, and John N Weinstein. 2004. “Mistaken Identifiers: Gene Name Errors Can Be Introduced Inadvertently When Using Excel in Bioinformatics.” BMC Bioinformatics 5: 80.
Zhu, Anqi, Joseph G Ibrahim, and Michael I Love. 2019. “Heavy-Tailed Prior Distributions for Sequence Count Data: Removing the Noise and Preserving Large Differences.” Bioinformatics 35: 2084–92.
Ziemann, Mark, Yotam Eren, and Assam El-Osta. 2016. “Gene Name Errors Are Widespread in the Scientific Literature.” Genome Biol. 17 (1): 1–3.
LS0tCnRpdGxlOiA+CiAgVHJhbnNjcmlwdG9tZSBEYXRhIEFuYWx5c2lzIC0gaGFuZHMtb24gc2Vzc2lvbgpzdWJ0aXRsZTogPgogIE1TRSAtIE1vZHVsZSBHZW5ldGljIEVwaWRlbWlvbG9neQogIDxwIGFsaWduPSJjZW50ZXIiPgogIDwvcD4KICA8YSBocmVmPSJodHRwczovL3d3dy51bmltZWRpemluLW1haW56LmRlL2ltYmVpL3N0YXJ0c2VpdGUvbXNlLyI+PGltZyBzcmM9ImltYWdlcy9tc2VfbG9nby5qcGciIGFsdD0iIiBoZWlnaHQ9IjEwMCIvPjwvYT4KYXV0aG9yOgotIG5hbWU6IDxhIGhyZWY9Imh0dHBzOi8vZmVkZXJpY29tYXJpbmkuZ2l0aHViLmlvIj5GZWRlcmljbyBNYXJpbmkgKG1hcmluaWZAdW5pLW1haW56LmRlKTwvYT48YnI+PGEgaHJlZj0iaHR0cHM6Ly93d3cudW5pbWVkaXppbi1tYWluei5kZS9pbWJlaS8iPklNQkVJLCBVbml2ZXJzaXR5IE1lZGljYWwgQ2VudGVyIE1haW56PC9hPjxicj48YSBocmVmPSJodHRwczovL3R3aXR0ZXIuY29tL0ZlZGVCaW9pbmZvIj5gciBpY29uczo6Zm9udGF3ZXNvbWUoJ3R3aXR0ZXInKWAgYEBGZWRlQmlvaW5mb2A8L2E+Ci0gbmFtZTogQWxpY2lhIFNjaHVsemUgKGFscG9wbGF3QHVuaS1tYWluei5kZSk8YnI+PGEgaHJlZj0iaHR0cHM6Ly93d3cudW5pbWVkaXppbi1tYWluei5kZS9pbWJlaS8iPklNQkVJLCBVbml2ZXJzaXR5IE1lZGljYWwgQ2VudGVyIE1haW56PC9hPjxicj4KZGF0ZTogIjIwMjQvMDcvMDMtMDQiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiBjb3NtbwogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgY29sbGFwc2UgPSBUUlVFLAogIGNvbW1lbnQgPSAiIyIsCiAgZXJyb3IgPSBGQUxTRSwKICB3YXJuaW5nID0gRkFMU0UsCiAgbWVzc2FnZSA9IEZBTFNFCikKYGBgCgo8IS0tIDxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiAtLT4KPCEtLSAgIHNyYz0iaHR0cDovL2Nkbi5tYXRoamF4Lm9yZy9tYXRoamF4L2xhdGVzdC9NYXRoSmF4LmpzP2NvbmZpZz1UZVgtQU1TLU1NTF9IVE1Mb3JNTUwiPiAtLT4KPCEtLSA8L3NjcmlwdD4gLS0+CgoKYGBge3Igc3R5bGUsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9ImFzaXMiLCBldmFsID0gVFJVRX0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICAgIGxpYnJhcnkoQmlvY1N0eWxlKQogICAgbGlicmFyeShrbml0cikKICAgIGxpYnJhcnkocm1hcmtkb3duKQp9KQpvcHRpb25zKHdpZHRoID0gODApCgpldmFsX2FsbCA8LSBUUlVFCgpldmFsX2R0ZSA8LSBGQUxTRQpldmFsX2R0dSA8LSBGQUxTRQoKb3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDYsIGV2YWwgPSBldmFsX2FsbCkKYGBgCgojIEludHJvZHVjdGlvbgoKSW4gdGhpcyB0dXRvcmlhbCB3ZSB3YWxrIHRocm91Z2ggYSBnZW5lLWxldmVsIFJOQS1zZXEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24KYW5hbHlzaXMgdXNpbmcgQmlvY29uZHVjdG9yIHBhY2thZ2VzLiBXZSBzdGFydCBmcm9tIHRoZSBnZW5lLXZzLXNhbXBsZSBjb3VudAptYXRyaXgsIGFuZCB0aHVzIGFzc3VtZSB0aGF0IHRoZSByYXcgcmVhZHMgaGF2ZSBhbHJlYWR5IGJlZW4gcXVhbGl0eSBjb250cm9sbGVkCmFuZCB0aGF0IHRoZSBnZW5lIGV4cHJlc3Npb24gaGFzIGJlZW4gcXVhbnRpZmllZCAoZWl0aGVyIHVzaW5nIGFsaWdubWVudCBhbmQKY291bnRpbmcsIG9yIGJ5IGFwcGx5aW5nIGFuIGFsaWdubWVudC1mcmVlIHF1YW50aWZpY2F0aW9uIHRvb2wpLiBXZSBwZXJmb3JtCmV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgKEVEQSkgZm9yIHF1YWxpdHkgYXNzZXNzbWVudCBhbmQgdG8gZXhwbG9yZSB0aGUKcmVsYXRpb25zaGlwIGJldHdlZW4gc2FtcGxlcywgdGhlbiBwZXJmb3JtIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24KYW5hbHlzaXMsIGFuZCB2aXN1YWxseSBleHBsb3JlIHRoZSByZXN1bHRzLgoKQmlvY29uZHVjdG9yIGhhcyBtYW55IHBhY2thZ2VzIHN1cHBvcnRpbmcgYW5hbHlzaXMgb2YgaGlnaC10aHJvdWdocHV0IHNlcXVlbmNlCmRhdGEsIGluY2x1ZGluZyBSTkEtc2VxLiBUaGUgcGFja2FnZXMgdGhhdCB3ZSB3aWxsIHVzZSBpbiB0aGlzIHR1dG9yaWFsIGluY2x1ZGUKY29yZSBwYWNrYWdlcyBtYWludGFpbmVkIGJ5IHRoZSBbQmlvY29uZHVjdG9yIGNvcmUKdGVhbV0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9hYm91dC9jb3JlLXRlYW0vKSBmb3IgaW1wb3J0aW5nIGFuZApwcm9jZXNzaW5nIHJhdyBzZXF1ZW5jaW5nIGRhdGEgYW5kIGxvYWRpbmcgZ2VuZSBhbm5vdGF0aW9ucy4gV2Ugd2lsbCBhbHNvIHVzZQpjb250cmlidXRlZCBwYWNrYWdlcyBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24gb2Ygc2VxdWVuY2luZwpkYXRhLiBUaHJvdWdoIHNjaGVkdWxlZCByZWxlYXNlcyBldmVyeSA2IG1vbnRocywgdGhlIEJpb2NvbmR1Y3RvciBwcm9qZWN0CmVuc3VyZXMgdGhhdCBhbGwgdGhlIHBhY2thZ2VzIHdpdGhpbiBhIHJlbGVhc2Ugd2lsbCB3b3JrIHRvZ2V0aGVyIGluIGhhcm1vbnkKKGhlbmNlIHRoZSAiY29uZHVjdG9yIiBtZXRhcGhvcikuIFRoZSBwYWNrYWdlcyB1c2VkIGluIHRoaXMgdHV0b3JpYWwgYXJlIGxvYWRlZAp3aXRoIHRoZSAqbGlicmFyeSogZnVuY3Rpb24gYW5kIGNhbiBiZSBpbnN0YWxsZWQgYnkgZm9sbG93aW5nIHRoZSBbQmlvY29uZHVjdG9yCnBhY2thZ2UgaW5zdGFsbGF0aW9uCmluc3RydWN0aW9uc10oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvaW5zdGFsbC8jaW5zdGFsbC1iaW9jb25kdWN0b3ItcGFja2FnZXMpLgpJZiB5b3UgdXNlIHRoZSByZXN1bHRzIGZyb20gYW4gUiBwYWNrYWdlIGluIHB1Ymxpc2hlZCByZXNlYXJjaCwgeW91IGNhbiBmaW5kIHRoZQpwcm9wZXIgY2l0YXRpb24gZm9yIHRoZSBzb2Z0d2FyZSBieSB0eXBpbmcgYGNpdGF0aW9uKCJwa2dOYW1lIilgLCB3aGVyZSB5b3UKd291bGQgc3Vic3RpdHV0ZSB0aGUgbmFtZSBvZiB0aGUgcGFja2FnZSBmb3IgYHBrZ05hbWVgLiBDaXRpbmcgbWV0aG9kcyBwYXBlcnMKaGVscHMgdG8gc3VwcG9ydCBhbmQgcmV3YXJkIHRoZSBpbmRpdmlkdWFscyB3aG8gcHV0IHRpbWUgaW50byBvcGVuIHNvdXJjZQpzb2Z0d2FyZSBmb3IgZ2Vub21pYyBkYXRhIGFuYWx5c2lzLgoKTWFueSBwYXJ0cyBvZiB0aGlzIHR1dG9yaWFsIGFyZSBiYXNlZCBvbiBhIHB1Ymxpc2hlZCBSTkEtc2VxIHdvcmtmbG93CmF2YWlsYWJsZSB2aWEgW0YxMDAwUmVzZWFyY2hdKGh0dHA6Ly9mMTAwMHJlc2VhcmNoLmNvbS9hcnRpY2xlcy80LTEwNzApCltATG92ZTIwMTVSTkFTZXFdIGFuZCBhcyBhIFtCaW9jb25kdWN0b3IKcGFja2FnZV0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL3dvcmtmbG93cy9odG1sL3JuYXNlcUdlbmUuaHRtbCkuCgpBbG9uZyB0aGlzIG5vdGVib29rIHRoYXQgeW91IGNhbiByZW5kZXIgeW91cnNlbGYgLSB3ZSB3aWxsIHNob3cgeW91IGhvdyBkdXJpbmcgdGhlCndvcmtzaG9wIC0geW91IHdpbGwgZW5jb3VudGVyIHNvbWUgc21hbGwgcXVpeiBzZWN0aW9ucyB0byBrZWVwIHVwIHdpdGggeW91ciBwcm9ncmVzcwp0aHJvdWdob3V0IHRoZSBlbnRpcmUgd29ya2Zsb3cuCgojIyBTZXR1cCByZWNvbW1lbmRhdGlvbgoKV2UgcmVjb21tZW5kIHlvdSB1c2UgdGhlIGZvbGxvd2luZyBzZXR1cCBmb3IgYW4gZWZmZWN0aXZlIGxlYXJuaW5nIGV4cGVyaWVuY2UKCiogQ2xvbmUgb3IgZG93bmxvYWQgdGhlIHdvcmtzaG9wIHJlcG8sIGF2YWlsYWJsZSBhdCBodHRwczovL2dpdGh1Yi5jb20vaW1iZWltYWluei9STkFzZXFGcmFua2Z1cnQyMDIzCgoqIChNYWtlIHN1cmUgeW91IGhhdmUgUi9SU3R1ZGlvL0Jpb2NvbmR1Y3RvciBhbGwgc2V0dXAsIGFzIGV4cGVjdGVkKQoKKiBXb3JrIGluIFJTdHVkaW8sIGlkZWFsbHkgY2xpY2tpbmcgb24gdGhlIFJTdHVkaW8gcHJvamVjdCBmaWxlCgoqIE9wZW4gdXAgdGhlIGBSTkFzZXFfcHJhY3RpY2FsLlJtZGAgZmlsZSwgaW5zaWRlIFJTdHVkaW8KCjwhLS0gKiBBcyBhIGNoZWNrLCByZWZlciB0byBgUk5Bc2VxX3ByYWN0aWNhbF9ydW5fYWxsLmh0bWxgIGZpbGUgLSBkb24ndCB1c2UgdGhhdCBkdXJpbmcgdGhlIHdvcmtzaG9wIC0tPgoKKiBFeGVjdXRlIHRoZSBjb2RlIGNodW5rcy9yZWFkIHRoZSBpbmZvcm1hdGlvbiBwcm92aWRlZAoKSGF2ZSBmdW4gbGVhcm5pbmcgLSB0b2dldGhlciEKCiMjIEV4cGVyaW1lbnRhbCBkYXRhCgpUaGUgZGF0YSB1c2VkIGluIHRoaXMgd29ya2Zsb3cgY29tZXMgZnJvbSBhbiBSTkEtc2VxIGV4cGVyaW1lbnQgCltAQWxhc29vMjAxOC1tYWNyb3BoYWdlXSwgaW4gd2hpY2ggdGhlIGF1dGhvcnMgaWRlbnRpZmllZCBzaGFyZWQgcXVhbnRpdGF0aXZlIAp0cmFpdCBsb2NpIChRVExzKSBmb3IgY2hyb21hdGluIGFjY2Vzc2liaWxpdHkgYW5kIGdlbmUgZXhwcmVzc2lvbiBpbiBodW1hbiAKbWFjcm9waGFnZXMgZXhwb3NlZCB0byBJRk5nYW1tYSwgU2FsbW9uZWxsYSBhbmQgSUZOZ2FtbWEgcGx1cyBTYWxtb25lbGxhLiAKUHJvY2Vzc2VkIGRhdGEgZnJvbSBhIHN1YnNldCBvZiAyNCBzYW1wbGVzIGZyb20gdGhpcyBleHBlcmltZW50IChzaXggZmVtYWxlIApkb25vcnMsIHdpdGggZm91ciB0cmVhdG1lbnRzIGVhY2gpIGlzIGF2YWlsYWJsZSB2aWEgdGhlIApgciBCaW9jcGtnKCJtYWNyb3BoYWdlIilgIFIvQmlvY29uZHVjdG9yIHBhY2thZ2UuIApJbiBwYXJ0aWN1bGFyLCB0aGUgcGFja2FnZSBjb250YWlucyBvdXRwdXQgZnJvbSAqU2FsbW9uKiBbQFBhdHJvMjAxN1NhbG1vbl0sIAphcyB3ZWxsIGFzIGEgbWV0YWRhdGEgZmlsZS4gTW9yZSBpbmZvcm1hdGlvbiBhYm91dCBob3cgdGhlIHJhdyBkYXRhIHdhcyAKcHJvY2Vzc2VkIGlzIGF2YWlsYWJsZSBmcm9tIHRoZSBwYWNrYWdlIApbdmlnbmV0dGVdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvZGF0YS9leHBlcmltZW50L3ZpZ25ldHRlcy9tYWNyb3BoYWdlL2luc3QvZG9jL21hY3JvcGhhZ2UuaHRtbCkuCgpXZSBzdGFydCBieSBzZXR0aW5nIHRoZSBwYXRoIHRvIHRoZSBmb2xkZXIgY29udGFpbmluZyB0aGUgcXVhbnRpZmljYXRpb25zIAoodGhlIG91dHB1dCBmb2xkZXJzIGZyb20gKlNhbG1vbiopLiBTaW5jZSB0aGVzZSBhcmUgcHJvdmlkZWQgd2l0aCBhbiBSIHBhY2thZ2UsCndlIHdpbGwgcG9pbnQgdG8gdGhlIGBleHRkYXRhYCBzdWJmb2xkZXIgb2YgdGhlIGluc3RhbGxlZCBwYWNrYWdlLiBGb3IgYSB0eXBpY2FsIAphbmFseXNpcyBvZiB5b3VyIG93biBkYXRhLCB5b3Ugd291bGQgcG9pbnQgZGlyZWN0bHkgdG8gYSBmb2xkZXIgb24geW91ciBzdG9yYWdlIApzeXN0ZW0gKGkuZS4sIG5vdCB1c2luZyBgc3lzdGVtLmZpbGUoKWApLgoKYGBge3IgbGlzdGZpbGVzfQpsaWJyYXJ5KG1hY3JvcGhhZ2UpCmRpciA8LSBzeXN0ZW0uZmlsZSgiZXh0ZGF0YSIsIHBhY2thZ2UgPSAibWFjcm9waGFnZSIpCmxpc3QuZmlsZXMoZGlyKQpgYGAKClRPRE86IG9wZW4gdXAgdGhhdCBkaXJlY3RvcnkgdG8gImJldHRlciBzZWUgd2hhdCBpcyBpbiBpdCI/IElkZWE6IGl0IGNhbiBnaXZlIHRoZW0gYSBzZW5zZSBmb3IgdGhlICp3aGF0IGRvIEkgZXhwZWN0IHRvIGdldCB0aGVyZT8qCgojIFJlYWRpbmcgdGhlIG1ldGFkYXRhCgpGaXJzdCwgd2Ugd2lsbCByZWFkIHRoZSBtZXRhZGF0YSBmb3IgdGhlIGV4cGVyaW1lbnQuIFRoZSBtYWluIGFubm90YXRpb25zIG9mIAppbnRlcmVzdCBmb3IgdGhpcyB0dXRvcmlhbCBhcmUgYGNvbmRpdGlvbl9uYW1lYCwgd2hpY2ggcmVwcmVzZW50cyB0aGUgCnRyZWF0bWVudCBvZiB0aGUgc2FtcGxlIChuYWl2ZSwgSUZOIGdhbW1hLCBTYWxtb25lbGxhLCBJRk4gZ2FtbWErU2FsbW9uZWxsYSkgYW5kIApgbGluZV9pZGAsIHdoaWNoIHJlcHJlc2VudHMgdGhlIGRvbm9yIElELiBUaGUgCnNhbXBsZSBpZGVudGlmaWVyIGlzIGdpdmVuIGJ5IHRoZSBgbmFtZXNgIGNvbHVtbiwgYW5kIHdpbGwgYmUgdXNlZCB0byBtYXRjaCB0aGUgCm1ldGFkYXRhIHRhYmxlIHRvIHRoZSBxdWFudGlmaWNhdGlvbnMuCgpgYGB7ciByZWFkY29sZGF0YX0KY29sZGF0YSA8LSByZWFkLmNzdihmaWxlLnBhdGgoZGlyLCAiY29sZGF0YS5jc3YiKSlbLCBjKDEsIDIsIDMsIDUpXQpkaW0oY29sZGF0YSkKY29sZGF0YQpgYGAKCkluIGFkZGl0aW9uIHRvIHRoZSBgbmFtZXNgIGNvbHVtbiwgYHIgQmlvY3BrZygidHhpbWV0YSIpYCwgd2hpY2ggd2Ugd2lsbCB1c2UgdG8gcmVhZCB0aGUgCnF1YW50aWZpY2F0aW9uIGRhdGEsIHJlcXVpcmVzIHRoYXQgYGNvbGRhdGFgIGhhcyBhIGNvbHVtbiBuYW1lZCBgZmlsZXNgLCAKcG9pbnRpbmcgdG8gdGhlICpTYWxtb24qIG91dHB1dCAodGhlIGBxdWFudC5zZmAgZmlsZSkgZm9yIHRoZSByZXNwZWN0aXZlIHNhbXBsZXMuCgpgYGB7ciBhZGRmaWxlc30KY29sZGF0YSRmaWxlcyA8LSBmaWxlLnBhdGgoZGlyLCAicXVhbnRzIiwgY29sZGF0YSRuYW1lcywgInF1YW50LnNmLmd6IikKaGVhZChjb2xkYXRhKQphbGwoZmlsZS5leGlzdHMoY29sZGF0YSRmaWxlcykpCmBgYAoKTm93IHdlIGhhdmUgZXZlcnl0aGluZyB3ZSBuZWVkLCBhbmQgY2FuIGltcG9ydCB0aGUgcXVhbnRpZmljYXRpb25zIHdpdGggKnR4aW1ldGEqLgpJbiB0aGlzIHByb2Nlc3MsIHdlIHdpbGwgc2VlIHRoYXQgKnR4aW1ldGEqIGF1dG9tYXRpY2FsbHkgaWRlbnRpZmllcyB0aGUgCnNvdXJjZSBhbmQgdmVyc2lvbiBvZiB0aGUgdHJhbnNjcmlwdG9tZSByZWZlcmVuY2UgdGhhdCB3YXMgdXNlZCBmb3IgdGhlIApxdWFudGlmaWNhdGlvbiwgYW5kIGFkZHMgc29tZSBtZXRhZGF0YS4KVGhlIGltcG9ydGVkIGRhdGEgd2lsbCBiZSBzdG9yZWQgaW4gYSAqU3VtbWFyaXplZEV4cGVyaW1lbnQqIGNvbnRhaW5lci4KCiMgSW1wb3J0aW5nIHF1YW50aWZpY2F0aW9ucyBpbnRvIFIKCldlIHdpbGwgCm5leHQgcmVhZCB0aGUgKlNhbG1vbiogcXVhbnRpZmljYXRpb25zIHByb3ZpZGVkIGluIHRoZSBgciBCaW9jcGtnKCJtYWNyb3BoYWdlIilgIApwYWNrYWdlIGludG8gUiBhbmQgc3VtbWFyaXplIHRoZSBleHBlY3RlZCBjb3VudHMgb24gdGhlIGdlbmUgbGV2ZWwuIApBIHNpbXBsZSB3YXkgdG8gaW1wb3J0IHJlc3VsdHMgZnJvbSBhIHZhcmlldHkgb2YgdHJhbnNjcmlwdCBhYnVuZGFuY2UgCmVzdGltYXRpb24gdG9vbHMgaW50byBSIGlzIHByb3ZpZGVkIGJ5IHRoZSBgciBCaW9jcGtnKCJ0eGltcG9ydCIpYCBhbmQgCmByIEJpb2Nwa2coInR4aW1ldGEiKWAgcGFja2FnZXMuIEhlcmUsICp0eGltcG9ydCogcmVhZHMgdGhlIHF1YW50aWZpY2F0aW9ucyBpbnRvIGEKbGlzdCBvZiBtYXRyaWNlcywgd2hpbGUgKnR4aW1ldGEqIGluc3RlYWQgYWdncmVnYXRlcyB0aGUgaW5mb3JtYXRpb24gaW50byBhCipTdW1tYXJpemVkRXhwZXJpbWVudCogb2JqZWN0LCBhbmQgYWxzbyBhdXRvbWF0aWNhbGx5IGFkZHMgYWRkaXRpb25hbAphbm5vdGF0aW9ucyBmb3IgdGhlIGZlYXR1cmVzLiBCb3RoIHBhY2thZ2VzIGNhbiByZXR1cm4gcXVhbnRpZmljYXRpb25zIG9uIHRoZQp0cmFuc2NyaXB0IGxldmVsIG9yIGFnZ3JlZ2F0ZSB0aGVtIG9uIHRoZSBnZW5lIGxldmVsLiBUaGV5IGFsc28gY2FsY3VsYXRlCmF2ZXJhZ2UgdHJhbnNjcmlwdCBsZW5ndGhzIGZvciBlYWNoIGdlbmUgYW5kIGVhY2ggc2FtcGxlLCB3aGljaCBjYW4gYmUgdXNlZCBhcwpvZmZzZXRzIHRvIGltcHJvdmUgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIGJ5IGFjY291bnRpbmcgZm9yCmRpZmZlcmVudGlhbCBpc29mb3JtIHVzYWdlIGFjcm9zcyBzYW1wbGVzIFtAU29uZXNvbjIwMTVEaWZmZXJlbnRpYWxdLgoKVGhlIGNvZGUgYmVsb3cgaW1wb3J0cyB0aGUgKlNhbG1vbiogcXVhbnRpZmljYXRpb25zIGludG8gUiB1c2luZyB0aGUgKnR4aW1ldGEqCnBhY2thZ2UuIE5vdGUgaG93IHRoZSB0cmFuc2NyaXB0b21lIHRoYXQgd2FzIHVzZWQgZm9yIHRoZSBxdWFudGlmaWNhdGlvbiBpcwphdXRvbWF0aWNhbGx5IHJlY29nbml6ZWQgYW5kIHVzZWQgdG8gYW5ub3RhdGUgdGhlIHJlc3VsdGluZyBkYXRhIG9iamVjdC4gSW4Kb3JkZXIgZm9yIHRoaXMgdG8gd29yaywgKnR4aW1ldGEqIHJlcXVpcmVzIHRoYXQgdGhlIG91dHB1dCBmb2xkZXIgc3RydWN0dXJlIGZyb20KU2FsbW9uIGlzIHJldGFpbmVkLCBzaW5jZSBpdCByZWFkcyBpbmZvcm1hdGlvbiBmcm9tIHRoZSBhc3NvY2lhdGVkIGxvZyBmaWxlcyBpbgphZGRpdGlvbiB0byB0aGUgcXVhbnRpZmllZCBhYnVuZGFuY2VzIHRoZW1zZWx2ZXMuIAoKYGBge3IgaW1wb3J0c3R9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgICBsaWJyYXJ5KHR4aW1ldGEpCiAgICBsaWJyYXJ5KERFU2VxMikKICAgIGxpYnJhcnkob3JnLkhzLmVnLmRiKQogICAgbGlicmFyeShTdW1tYXJpemVkRXhwZXJpbWVudCkKfSkKCiMjIEltcG9ydCBxdWFudGlmaWNhdGlvbnMgb24gdGhlIHRyYW5zY3JpcHQgbGV2ZWwKc3QgPC0gdHhpbWV0YShjb2xkYXRhID0gY29sZGF0YSwgdHlwZSA9ICJzYWxtb24iLCBkcm9wSW5mUmVwcyA9IFRSVUUpCgpzdApgYGAKCldlIHNlZSB0aGF0ICp0eGltZXRhKiBoYXMgaWRlbnRpZmllZCB0aGUgdHJhbnNjcmlwdG9tZSB1c2VkIGZvciB0aGUgCnF1YW50aWZpY2F0aW9uIGFzIGBHRU5DT0RFIC0gSG9tbyBzYXBpZW5zIC0gcmVsZWFzZSAyOWAuIEhvdyBkaWQgdGhpcyBoYXBwZW4/CkluIGZhY3QsIHRoZSBvdXRwdXQgZGlyZWN0b3J5IGZyb20gKlNhbG1vbiogY29udGFpbnMgbXVjaCBtb3JlIGluZm9ybWF0aW9uIHRoYW4gCmp1c3QgdGhlIGBxdWFudC5zZmAgZmlsZSEgKGFzIG1lbnRpb25lZCBhYm92ZSwgdGhpcyBtZWFucyB0aGF0IGl0IGlzIG5vdCAKYWR2aXNhYmxlIHRvIG1vdmUgZmlsZXMgb3V0IG9mIHRoZSBmb2xkZXIsIG9yIHRvIHNoYXJlIG9ubHkgdGhlIGBxdWFudC5zZmAgCmZpbGUsIHNpbmNlIHRoZSBjb250ZXh0IGlzIGxvc3QpOgoKYGBge3IgbGlzdGlucXVhbnRzfQpsaXN0LmZpbGVzKGZpbGUucGF0aChkaXIsICJxdWFudHMiLCBjb2xkYXRhJG5hbWVzWzFdKSwgcmVjdXJzaXZlID0gVFJVRSkKYGBgCgpJbiBwYXJ0aWN1bGFyLCB0aGUgYG1ldGFfaW5mby5qc29uYCBmaWxlIGNvbnRhaW5zIGEgaGFzaCBjaGVja3N1bSwgd2hpY2ggaXMgCmRlcml2ZWQgZnJvbSB0aGUgc2V0IG9mIHRyYW5zY3JpcHRzIHVzZWQgYXMgcmVmZXJlbmNlIGR1cmluZyB0aGUgcXVhbnRpZmljYXRpb24gCmFuZCB3aGljaCBsZXRzICp0eGltZXRhKiBpZGVudGlmeSB0aGUgcmVmZXJlbmNlIHNvdXJjZSAoYnkgY29tcGFyaW5nIHRvIGEgCnRhYmxlIG9mIHRoZXNlIGhhc2ggY2hlY2tzdW1zIGZvciBjb21tb25seSB1c2VkIHJlZmVyZW5jZXMpLgoKYGBge3IgbWV0YWluZm99CnJqc29uOjpmcm9tSlNPTihmaWxlID0gZmlsZS5wYXRoKGRpciwgInF1YW50cyIsIGNvbGRhdGEkbmFtZXNbMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXV4X2luZm8iLCAibWV0YV9pbmZvLmpzb24iKSkKYGBgCgpMb29raW5nIGF0IHRoZSBzaXplIG9mIHRoZSAqU3VtbWFyaXplZEV4cGVyaW1lbnQqIG9iamVjdCAoMjA1LDg3MCByb3dzISkgYXMgd2VsbCAKYXMgdGhlIHJvdyBuYW1lcywgd2Ugc2VlIHRoYXQgdGhpcyBvYmplY3QgY29udGFpbnMgdHJhbnNjcmlwdC1sZXZlbCBpbmZvcm1hdGlvbi4KVGhlIGFzc2F5cyBhcmUgY3JlYXRlZCBieSBkaXJlY3RseSBpbXBvcnRpbmcgdGhlIHZhbHVlcyBmcm9tIHRoZSBgcXVhbnQuc2ZgIApmaWxlcyBhbmQgY29tYmluaW5nIHRoaXMgaW5mb3JtYXRpb24gYWNyb3NzIHRoZSAyNCBzYW1wbGVzOgoKKiBjb3VudHMgLSBgTnVtUmVhZHNgIGNvbHVtbgoqIGFidW5kYW5jZSAtIGBUUE1gIGNvbHVtbgoqIGxlbmd0aCAtIGBFZmZlY3RpdmVMZW5ndGhgIGNvbHVtbgoKV2UgY2FuIGFjY2VzcyBhbnkgb2YgdGhlIGFzc2F5cyB2aWEgdGhlIGBhc3NheWAgZnVuY3Rpb246CgpgYGB7ciBoZWFkc3R9CmhlYWQoYXNzYXkoc3QsICJjb3VudHMiKSwgMykKYGBgCgpZb3UgbWF5IGhhdmUgbm90ZWQgdGhhdCBgc3RgIGlzIGluIGZhY3QgYSAqUmFuZ2VkU3VtbWFyaXplZEV4cGVyaW1lbnQqIApvYmplY3QgKHJhdGhlciB0aGFuICJqdXN0IiBhICpTdW1tYXJpemVkRXhwZXJpbWVudCogb2JqZWN0KS4gV2hhdCBkb2VzIHRoaXMgbWVhbj8KTGV04oCZcyBsb29rIGF0IHRoZSBpbmZvcm1hdGlvbiB3ZSBoYXZlIGFib3V0IHRoZSByb3dzICh0cmFuc2NyaXB0cykgaW4gdGhlIG9iamVjdDoKCmBgYHtyIHJyc3R9CnJvd1JhbmdlcyhzdCkKYGBgCgpCeSBrbm93aW5nIHRoZSBzb3VyY2UgYW5kIHZlcnNpb24gb2YgdGhlIHJlZmVyZW5jZSB1c2VkIGZvciB0aGUgcXVhbnRpZmljYXRpb24sIAoqdHhpbWV0YSogd2FzIGFibGUgdG8gcmV0cmlldmUgdGhlIGFubm90YXRpb24gZmlsZXMgYW5kIGRlY29yYXRlIHRoZSBvYmplY3Qgd2l0aCAKaW5mb3JtYXRpb24gYWJvdXQgdGhlIHRyYW5zY3JpcHRzLCBzdWNoIGFzIHRoZSBjaHJvbW9zb21lIGFuZCBwb3NpdGlvbiwgYW5kIHRoZSAKY29ycmVzcG9uZGluZyBnZW5lIElELiBJbXBvcnRhbnRseSwgKlNhbG1vbiogZGlkIG5vdCB1c2UgKG9yIGtub3cgYWJvdXQpIGFueSBvZiAKdGhpcyBkdXJpbmcgdGhlIHF1YW50aWZpY2F0aW9uISBJdCBuZWVkcyBvbmx5IHRoZSB0cmFuc2NyaXB0IHNlcXVlbmNlcy4gCklmIHdlIGp1c3Qgd2FudCB0aGUgYW5ub3RhdGlvbiBjb2x1bW5zLCB3aXRob3V0IHRoZSByYW5nZXMsIHdlIGNhbiBnZXQgdGhvc2UgCndpdGggdGhlIGByb3dEYXRhYCBhY2Nlc3NvcjoKCmBgYHtyIHJkc3R9CnJvd0RhdGEoc3QpCmBgYAoKU2ltaWxhciB0byB0aGUgcm93IGFubm90YXRpb25zIGluIGByb3dEYXRhYCwgdGhlICpTdW1tYXJpemVkRXhwZXJpbWVudCogCm9iamVjdCBjb250YWlucyBzYW1wbGUgYW5ub3RhdGlvbnMgaW4gdGhlIGBjb2xEYXRhYCBzbG90LgoKYGBge3IgY2RzdH0KY29sRGF0YShzdCkKYGBgCgojIyMgUXVpeiB7LX0KCiogSG93IG1hbnkgc2FtcGxlcyBhcmUgdGhlcmUgaW4gdGhpcyBgbWFjcm9waGFnZWAgZGF0YXNldD8gSG93IG1hbnkgYXJlIHRoZXJlIGluIHlvdXIgKHR5cGljYWwpIFJOQS1zZXEgZXhwZXJpbWVudD8KKiBJcyB0aGUgcXVhbnRpZmljYXRpb24gZG9uZSBhdCB0aGUgdHJhbnNjcmlwdCBsZXZlbCBvciBhdCB0aGUgZ2VuZSBsZXZlbD8gSG93IGNhbiB5b3UgZ28gZnJvbSBvbmUgdG8gdGhlIG90aGVyPyBBbmQgdmljZSB2ZXJzYT8KKiBXaGF0IGlzICJiZXR0ZXIiPyBUaGluayBvZiBER0UsIERUVSwgRFRFLi4uIGFuZCBvZiB5b3VyIGV4cGVyaW1lbnRhbCBzZXR0aW5nCgojIFN1bW1hcml6aW5nIG9uIHRoZSBnZW5lIGxldmVsCgpBcyB3ZSBzYXcsIHRoZSBmZWF0dXJlcyBpbiB0aGUgKlN1bW1hcml6ZWRFeHBlcmltZW50KiBvYmplY3QgYWJvdmUgYXJlIGluZGl2aWR1YWwgCnRyYW5zY3JpcHRzLCByYXRoZXIgdGhhbiBnZW5lcy4KT2Z0ZW4sIGhvd2V2ZXIsIHdlIHdhbnQgdG8gZG8gYW5hbHlzaXMgb24gdGhlIGdlbmUgbGV2ZWwsIHNpbmNlIHRoZSBnZW5lLWxldmVsIAphYnVuZGFuY2VzIGFyZSBtb3JlIHJvYnVzdCBhbmQgc29tZXRpbWVzIG1vcmUgaW50ZXJwcmV0YWJsZSB0aGFuIHRyYW5zY3JpcHQtbGV2ZWwgCmFidW5kYW5jZXMuClRoZSBgcm93RGF0YWAgY29udGFpbnMgdGhlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjb3JyZXNwb25kaW5nIGdlbmUgZm9yIGVhY2ggCnRyYW5zY3JpcHQsIGluIHRoZSBgZ2VuZV9pZGAgY29sdW1uLCBhbmQgKnR4aW1ldGEqIHByb3ZpZGVzIGEgZnVuY3Rpb24gCnRvIHN1bW1hcml6ZSBvbiB0aGUgZ2VuZSBsZXZlbDoKCiogQ291bnRzIGFyZSBhZGRlZCB1cAoqIFRQTXMgYXJlIGFkZGVkIHVwCiogVHJhbnNjcmlwdCBsZW5ndGhzIGFyZSBhZGRlZCB1cCBhZnRlciB3ZWlnaHRpbmcgYnkgdGhlIHJlc3BlY3RpdmUgdHJhbnNjcmlwdCBUUE1zCgpgYGB7ciBzdW1tYXJpemV0b2dlbmV9CiMjIFN1bW1hcml6ZSBxdWFudGlmaWNhdGlvbnMgb24gdGhlIGdlbmUgbGV2ZWwKc2cgPC0gdHhpbWV0YTo6c3VtbWFyaXplVG9HZW5lKHN0KQpzZwoKIyBjb21wYXJlIGUuZy4gdG8gCnN0CmBgYAoKTm93IHdlIGhhdmUgYSBuZXcgYFJhbmdlZFN1bW1hcml6ZWRFeHBlcmltZW50YCBvYmplY3QsIHdpdGggb25lIHJvdyBwZXIgZ2VuZS4KVGhlIHJvdyByYW5nZXMgaGF2ZSBiZWVuIHN1bW1hcml6ZWQgYXMgd2VsbCwgYW5kIGNhbiBiZSB1c2VkIGZvciBzdWJzZXR0aW5nIAphbmQgaW50ZXJwcmV0YXRpb24ganVzdCBhcyBmb3IgdGhlIHRyYW5zY3JpcHRzLgoKQXQgdGhpcyBwb2ludCwgdGhlIG9ubHkgaW5mb3JtYXRpb24gd2UgaGF2ZSBhYm91dCB0aGUgZ2VuZXMgaW4gb3VyIGRhdGEgc2V0LCAKYXBhcnQgZnJvbSB0aGVpciBnZW5vbWljIGxvY2F0aW9uIGFuZCB0aGUgYXNzb2NpYXRlZCB0cmFuc2NyaXB0IElEcywgaXMgdGhlIApFbnNlbWJsIElELgpPZnRlbiB3ZSBuZWVkIGFkZGl0aW9uYWwgYW5ub3RhdGlvbnMsIHN1Y2ggYXMgZ2VuZSBzeW1ib2xzLgpCaW9jb25kdWN0b3IgcHJvdmlkZXMgYSByYW5nZSBvZiBhbm5vdGF0aW9uIHBhY2thZ2VzOgoKKiBgT3JnRGJgIHBhY2thZ2VzLCBwcm92aWRpbmcgZ2VuZS1iYXNlZCBhbm5vdGF0aW9ucyBmb3IgYSBnaXZlbiBvcmdhbmlzbQoqIGBUeERiYCBhbmQgYEVuc0RiYCBwYWNrYWdlcywgcHJvdmlkaW5nIHRyYW5zY3JpcHQgcmFuZ2VzIGZvciBhIGdpdmVuIGdlbm9tZSBidWlsZAoqIGBCU2dlbm9tZWAgcGFja2FnZXMsIHByb3ZpZGluZyB0aGUgZ2Vub21lIHNlcXVlbmNlIGZvciBhIGdpdmVuIGdlbm9tZSBidWlsZAoKRm9yIG91ciBwdXJwb3NlcyBoZXJlLCB0aGUgYXBwcm9wcmlhdGUgYE9yZ0RiYCBwYWNrYWdlIGlzIHRoZSBtb3N0IHN1aXRhYmxlLCAKc2luY2UgaXQgY29udGFpbnMgZ2VuZS1jZW50cmljIElEIGNvbnZlcnNpb24gdGFibGVzLgpTaW5jZSB0aGlzIGlzIGh1bWFuIGRhdGEsIHdlIHdpbGwgdXNlIHRoZSBgciBCaW9jcGtnKCJvcmcuSHMuZWcuZGIiKWAgcGFja2FnZS4KCmBgYHtyIGFkZHN5bWJvbH0KIyMgQWRkIGdlbmUgc3ltYm9scwpzZyA8LSB0eGltZXRhOjphZGRJZHMoc2csICJTWU1CT0wiLCBnZW5lID0gVFJVRSkKc2cKaGVhZChyb3dEYXRhKHNnKSkKYGBgCgpUbyBzZWUgYSBsaXN0IG9mIHRoZSBwb3NzaWJsZSBjb2x1bW5zLCB1c2UgdGhlIGBjb2x1bW5zYCBmdW5jdGlvbiBmcm9tIHRoZSAKYHIgQmlvY3BrZygiQW5ub3RhdGlvbkRiaSIpYCBwYWNrYWdlOgoKYGBge3IgbGlzdGNvbHN9CkFubm90YXRpb25EYmk6OmNvbHVtbnMob3JnLkhzLmVnLmRiKQpgYGAKCldlIGNhbiBldmVuIGFkZCBhbm5vdGF0aW9ucyB3aGVyZSB3ZSBleHBlY3QgKGFuZCB3b3VsZCBsaWtlIHRvIHJldGFpbikgCm11bHRpcGxlIG1hcHBpbmcgdmFsdWVzLCBlLmcuLCBhc3NvY2lhdGVkIEdPIHRlcm1zOgoKYGBge3IgYWRkZ299CnNnIDwtIGFkZElkcyhzZywgIkdPIiwgbXVsdGlWYWxzID0gImxpc3QiLCBnZW5lID0gVFJVRSkKaGVhZChyb3dEYXRhKHNnKSkKYGBgCgpOb3RlIHRoYXQgKlNhbG1vbiogcmV0dXJucyAqZXN0aW1hdGVkKiBvciAqZXhwZWN0ZWQqIGNvdW50cywgd2hpY2ggYXJlIG5vdCBuZWNlc3NhcmlseQppbnRlZ2Vycy4gVGhleSBtYXkgbmVlZCB0byBiZSByb3VuZGVkIGJlZm9yZSB0aGV5IGFyZSBwYXNzZWQgdG8gY291bnQtYmFzZWQKc3RhdGlzdGljYWwgbWV0aG9kcy4gVG8gb2J0YWluIGNvbnNpc3RlbnQgcmVzdWx0cyB3aXRoIGRpZmZlcmVudApwaXBlbGluZXMsIHdlIHJvdW5kIHRoZSBlc3RpbWF0ZWQgY291bnRzIGhlcmUgKG5vdGUgdGhhdCBpbiBwcmFjdGljZSwgCmByIEJpb2Nwa2coIkRFU2VxMiIpYCB3aWxsIGF1dG9tYXRpY2FsbHkgcm91bmQgdGhlIGNvdW50cywgd2hpbGUgCmByIEJpb2Nwa2coImVkZ2VSIilgIHdpbGwgd29yayB3ZWxsIGFsc28gd2l0aCB0aGUgbm9uLWludGVnZXIgdmFsdWVzKS4KCmBgYHtyIHJvdW5kY291bnRzfQphc3NheShzZywgImNvdW50cyIpIDwtIHJvdW5kKGFzc2F5KHNnLCAiY291bnRzIikpCmBgYAoKIyMjIFF1aXogey19CgoqIENhbiB5b3Ugc3RhcnQgeW91ciBERSBhbmFseXNpcyB3aXRoIG5vcm1hbGl6ZWQgdmFsdWVzIHN1Y2ggYXMgVFBNcz8KKiBXaGVyZSBjYW4gSSBzdG9yZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzYW1wbGVzPwoqIFdoZXJlIGNhbiBJIHN0b3JlIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGZlYXR1cmVzPyAoV2hhdCBjYW4geW91ciBmZWF0dXJlcyBiZT8pCiogQ2FuIHlvdSB0aGluayBvZiBhIHdheSB0byByZWFkIGluIHRoZSByZXF1aXJlZCBpbmZvcm1hdGlvbiBpZiB5b3Ugc3RhcnQgKEFBQUFBQUFIKSBmcm9tIEV4Y2VsIGZpbGVzPwoKIyBSZXByZXNlbnRpbmcgY291bnRzIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBwYWNrYWdlcwoKQXQgdGhpcyBwb2ludCwgd2UgaGF2ZSBhIGdlbmUtbGV2ZWwgY291bnQgbWF0cml4LCBjb250YWluZWQgaW4gb3VyIAoqU3VtbWFyaXplZEV4cGVyaW1lbnQqIG9iamVjdC4gClRoaXMgaXMgYSBicmFuY2hpbmcgcG9pbnQgd2hlcmUgd2UgY291bGQgdXNlIGEgdmFyaWV0eSBvZgpCaW9jb25kdWN0b3IgcGFja2FnZXMgZm9yIGV4cGxvcmF0aW9uIGFuZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBvZiB0aGUgY291bnQKbWF0cml4LCBpbmNsdWRpbmcgYHIgQmlvY3BrZygiZWRnZVIiKWAgW0BSb2JpbnNvbjIwMDlFZGdlUl0sIApgciBCaW9jcGtnKCJERVNlcTIiKWAgW0BMb3ZlMjAxNE1vZGVyYXRlZF0sIGByIEJpb2Nwa2coImxpbW1hIilgIHdpdGggdGhlIHZvb20KbWV0aG9kIFtATGF3MjAxNFZvb21dLCBgciBCaW9jcGtnKCJEU1MiKWAgW0BXdTIwMTNOZXddLCBgciBCaW9jcGtnKCJFQlNlcSIpYApbQExlbmcyMDEzRUJTZXFdIGFuZCBgciBCaW9jcGtnKCJCYXlTZXEiKWAgW0BIYXJkY2FzdGxlMjAxMEJheVNlcV0uIFdlIHdpbGwKY29udGludWUgdXNpbmcgKkRFU2VxMiogYW5kICplZGdlUiouCgpCaW9jb25kdWN0b3Igc29mdHdhcmUgcGFja2FnZXMgb2Z0ZW4gZGVmaW5lIGFuZCB1c2UgYSBjdXN0b20gY2xhc3MgZm9yIHN0b3JpbmcKZGF0YSB0aGF0IG1ha2VzIHN1cmUgdGhhdCBhbGwgdGhlIG5lZWRlZCBkYXRhIHNsb3RzIGFyZSBjb25zaXN0ZW50bHkgcHJvdmlkZWQKYW5kIGZ1bGZpbGwgYW55IHJlcXVpcmVtZW50cy4gSW4gYWRkaXRpb24sIEJpb2NvbmR1Y3RvciBoYXMgZ2VuZXJhbCBkYXRhIGNsYXNzZXMKKHN1Y2ggYXMgdGhlICpTdW1tYXJpemVkRXhwZXJpbWVudCopIHRoYXQgY2FuIGJlIHVzZWQgdG8gbW92ZSBkYXRhIGJldHdlZW4KcGFja2FnZXMuIFRoZSBgciBCaW9jcGtnKCJERUZvcm1hdHMiKWAgcGFja2FnZSBjYW4gYmUgdXNlZnVsIGZvciBjb252ZXJ0aW5nCmJldHdlZW4gZGlmZmVyZW50IGNsYXNzZXMuIFRoZSBjb3JlIEJpb2NvbmR1Y3RvciBjbGFzc2VzIGFsc28gcHJvdmlkZSB1c2VmdWwKZnVuY3Rpb25hbGl0eTogZm9yIGV4YW1wbGUsIHN1YnNldHRpbmcgb3IgcmVvcmRlcmluZyB0aGUgcm93cyBvciBjb2x1bW5zIG9mIGEKKlN1bW1hcml6ZWRFeHBlcmltZW50KiBhdXRvbWF0aWNhbGx5IHN1YnNldHMgb3IgcmVvcmRlcnMgdGhlIGFzc29jaWF0ZWQKKnJvd1JhbmdlcyogYW5kICpjb2xEYXRhKiwgd2hpY2ggY2FuIGhlbHAgdG8gcHJldmVudCBhY2NpZGVudGFsIHNhbXBsZSBzd2Fwcwp0aGF0IHdvdWxkIG90aGVyd2lzZSBsZWFkIHRvIHNwdXJpb3VzIHJlc3VsdHMuIFdpdGggKlN1bW1hcml6ZWRFeHBlcmltZW50KiB0aGlzCmlzIGFsbCB0YWtlbiBjYXJlIG9mIGJlaGluZCB0aGUgc2NlbmVzLgoKRWFjaCBvZiB0aGUgcGFja2FnZXMgd2Ugd2lsbCB1c2UgZm9yIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGhhcyBhIHNwZWNpZmljIApjbGFzcyBvZiBvYmplY3QgdXNlZCB0byBzdG9yZSB0aGUgc3VtbWFyaXphdGlvbiBvZiB0aGUgUk5BLXNlcSBleHBlcmltZW50IGFuZCAKdGhlIGludGVybWVkaWF0ZSBxdWFudGl0aWVzIHRoYXQgYXJlIGNhbGN1bGF0ZWQgZHVyaW5nIHRoZSBzdGF0aXN0aWNhbCBhbmFseXNpcyAKb2YgdGhlIGRhdGEuICpERVNlcTIqIHVzZXMgYSAqREVTZXFEYXRhU2V0KiBhbmQgKmVkZ2VSKiB1c2VzIGEgKkRHRUxpc3QqLgoKIyMgVGhlICpERVNlcURhdGFTZXQqLCBzYW1wbGUgaW5mb3JtYXRpb24sIGFuZCB0aGUgZGVzaWduIGZvcm11bGEKCkluICpERVNlcTIqLCB0aGUgY3VzdG9tIGNsYXNzIGlzIGNhbGxlZCAqREVTZXFEYXRhU2V0Ki4gSXQgaXMgYnVpbHQgb24gdG9wIG9mCnRoZSAqU3VtbWFyaXplZEV4cGVyaW1lbnQqIGNsYXNzLCBhbmQgaXQgaXMgZWFzeSB0byBjb252ZXJ0IAoqU3VtbWFyaXplZEV4cGVyaW1lbnQqIG9iamVjdHMgaW50byAqREVTZXFEYXRhU2V0KiBvYmplY3RzLiBPbmUgb2YgdGhlIHR3byBtYWluCmRpZmZlcmVuY2VzIGNvbXBhcmVkIHRvIGEgKlN1bW1hcml6ZWRFeHBlcmltZW50KiBvYmplY3QgaXMgdGhhdCB0aGUgYGFzc2F5YCBzbG90CmNhbiBiZSBhY2Nlc3NlZCB1c2luZyB0aGUgYGNvdW50c2AgYWNjZXNzb3IgZnVuY3Rpb24sIGFuZCB0aGUgKkRFU2VxRGF0YVNldCoKY2xhc3MgZW5mb3JjZXMgdGhhdCB0aGUgdmFsdWVzIGluIHRoaXMgbWF0cml4IGFyZSBub24tbmVnYXRpdmUgaW50ZWdlcnMuCgpBIHNlY29uZCBkaWZmZXJlbmNlIGlzIHRoYXQgdGhlICpERVNlcURhdGFTZXQqIGhhcyBhbiBhc3NvY2lhdGVkICpkZXNpZ24KZm9ybXVsYSouIFRoZSBleHBlcmltZW50YWwgZGVzaWduIGlzIHNwZWNpZmllZCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBhbmFseXNpcywKYXMgaXQgd2lsbCBpbmZvcm0gbWFueSBvZiB0aGUgKkRFU2VxMiogZnVuY3Rpb25zIGhvdyB0byB0cmVhdCB0aGUgc2FtcGxlcyBpbiB0aGUKYW5hbHlzaXMgKG9uZSBleGNlcHRpb24gaXMgdGhlIHNpemUgZmFjdG9yIGVzdGltYXRpb24sIGkuZS4sIHRoZSBhZGp1c3RtZW50IGZvcgpkaWZmZXJpbmcgbGlicmFyeSBzaXplcywgd2hpY2ggZG9lcyBub3QgZGVwZW5kIG9uIHRoZSBkZXNpZ24gZm9ybXVsYSkuIFRoZQpkZXNpZ24gZm9ybXVsYSB0ZWxscyB3aGljaCBjb2x1bW5zIGluIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24gdGFibGUgKGBjb2xEYXRhYCkgCnNwZWNpZnkgdGhlIGV4cGVyaW1lbnRhbCBkZXNpZ24gYW5kIGhvdyB0aGVzZSBmYWN0b3JzIHNob3VsZCBiZSB1c2VkIGluIHRoZQphbmFseXNpcy4KCkxldCdzIHJlbWluZCBvdXJzZWx2ZXMgb2YgdGhlIGRlc2lnbiBvZiBvdXIgZXhwZXJpbWVudDoKCmBgYHtyIGNkc2d9CmNvbERhdGEoc2cpCmBgYAoKV2UgaGF2ZSBzYW1wbGVzIGZyb20gZm91ciBkaWZmZXJlbnQgY29uZGl0aW9ucywgYW5kIHNpeCBkb25vcnM6CgpgYGB7ciB0YWJjb2xkYXRhfQp0YWJsZShjb2xEYXRhKHNnKSRjb25kaXRpb25fbmFtZSkKdGFibGUoY29sRGF0YShzZykkbGluZV9pZCkKCiMgcG9zc2libGUgdG8gdXNlIGEgc2hvcnRjdXQKdGFibGUoc2ckbGluZV9pZCkKYGBgCgoKV2Ugd2FudCB0byBmaW5kIHRoZSBjaGFuZ2VzIGluIGdlbmUgZXhwcmVzc2lvbiB0aGF0IGNhbiBiZSBhc3NvY2lhdGVkIHdpdGgKdGhlIGRpZmZlcmVudCB0cmVhdG1lbnRzLCBidXQgd2UgYWxzbyB3YW50IHRvIGNvbnRyb2wgZm9yIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlCmRvbm9ycy4gVGhlIGRlc2lnbiB3aGljaCBhY2NvbXBsaXNoZXMgdGhpcyBpcyBvYnRhaW5lZCBieSB3cml0aW5nIApgfiBsaW5lX2lkICsgY29uZGl0aW9uX25hbWVgLiBCeSBpbmNsdWRpbmcgYGxpbmVfaWRgLCB0ZXJtcyB3aWxsIGJlIAphZGRlZCB0byB0aGUgbW9kZWwgd2hpY2gKYWNjb3VudCBmb3IgZGlmZmVyZW5jZXMgYWNyb3NzIGRvbm9ycywgYW5kIGJ5IGFkZGluZyBgY29uZGl0aW9uX25hbWVgIHdlIGdldCAKdGVybXMgcmVwcmVzZW50aW5nIHRoZSBkaWZmZXJlbnQgdHJlYXRtZW50IGVmZmVjdHMuCgoqKk5vdGU6KiogaXQgd2lsbCBiZSBoZWxwZnVsIGZvciB1cyBpZiB0aGUgZmlyc3QgbGV2ZWwgb2YgYSBmYWN0b3IgaXMgdGhlIApyZWZlcmVuY2UgbGV2ZWwgKGUuZy4gY29udHJvbCwgb3IgdW50cmVhdGVkIHNhbXBsZXMpLiBUaGUgcmVhc29uIGlzIHRoYXQgYnkKc3BlY2lmeWluZyB0aGlzLCBmdW5jdGlvbnMgZnVydGhlciBpbiB0aGUgcGlwZWxpbmUgY2FuIGJlIHVzZWQgYW5kIHdpbGwgZ2l2ZQpjb21wYXJpc29ucyBzdWNoIGFzICd0cmVhdG1lbnQgdnMgY29udHJvbCcsIHdpdGhvdXQgbmVlZGluZyB0byBzcGVjaWZ5CmFkZGl0aW9uYWwgYXJndW1lbnRzLgoKV2UgY2FuICpyZWxldmVsKiB0aGUgYGNvbmRpdGlvbl9uYW1lYCBmYWN0b3IgbGlrZSBzbzogCgpgYGB7ciByZWxldmVsc2d9CmNvbERhdGEoc2cpJGNvbmRpdGlvbl9uYW1lIDwtIGZhY3Rvcihjb2xEYXRhKHNnKSRjb25kaXRpb25fbmFtZSkKY29sRGF0YShzZykkY29uZGl0aW9uX25hbWUgPC0gcmVsZXZlbChjb2xEYXRhKHNnKSRjb25kaXRpb25fbmFtZSwgcmVmID0gIm5haXZlIikKY29sRGF0YShzZykkY29uZGl0aW9uX25hbWUKYGBgCgpZb3UgY2FuIHVzZSBSJ3MgZm9ybXVsYSBub3RhdGlvbiB0byBleHByZXNzIGFueSBmaXhlZC1lZmZlY3RzIGV4cGVyaW1lbnRhbApkZXNpZ24gZm9yICplZGdlUiogb3IgKkRFU2VxMiouIE5vdGUgdGhhdCB0aGVzZSBwYWNrYWdlcyB1c2UgdGhlIHNhbWUgZm9ybXVsYQpub3RhdGlvbiBhcywgZm9yIGluc3RhbmNlLCB0aGUgYGxtYCBmdW5jdGlvbiBvZiBiYXNlIFIuCgpVc2luZyB0aGUgYHIgQmlvY3BrZygiRXhwbG9yZU1vZGVsTWF0cml4IilgIFIvQmlvY29uZHVjdG9yIHBhY2thZ2UsIHdlIGNhbiAKcmVwcmVzZW50IG91ciBkZXNpZ24gaW4gYSBncmFwaGljYWwgd2F5OiAKCmBgYHtyIGVtbXZpcywgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4LCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkoRXhwbG9yZU1vZGVsTWF0cml4KQp2ZCA8LSBWaXN1YWxpemVEZXNpZ24oc2FtcGxlRGF0YSA9IGNvbERhdGEoc2cpLCAKICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbkZvcm11bGEgPSB+IGxpbmVfaWQgKyBjb25kaXRpb25fbmFtZSwKICAgICAgICAgICAgICAgICAgICAgIHRleHRTaXplRml0dGVkID0gNCkKdmQkcGxvdGxpc3QKdmQkY29vY2N1cnJlbmNlcGxvdHMKYGBgCgpXZSBjYW4gYWxzbyBvcGVuIHRoZSBpbnRlcmFjdGl2ZSBpbnRlcmZhY2UgdG8gZXhwbG9yZSBvdXIgZGVzaWduIGZ1cnRoZXI6IAoKYGBge3IgZW1tLCBldmFsPUZBTFNFfQpFeHBsb3JlTW9kZWxNYXRyaXgoc2FtcGxlRGF0YSA9IGNvbERhdGEoc2cpLCAKICAgICAgICAgICAgICAgICAgIGRlc2lnbkZvcm11bGEgPSB+IGxpbmVfaWQgKyBjb25kaXRpb25fbmFtZSkKYGBgCgoKVG8gZ2VuZXJhdGUgYSAqREVTZXFEYXRhU2V0KiBvYmplY3QgZnJvbSBhICpTdW1tYXJpemVkRXhwZXJpbWVudCogb2JqZWN0LCB3ZSAKb25seSBuZWVkIHRvIGFkZGl0aW9uYWxseSBwcm92aWRlIHRoZSBleHBlcmltZW50YWwgZGVzaWduIGluIHRlcm1zIG9mIGEgZm9ybXVsYS4KCmBgYHtyIGRzZHN9CmRkcyA8LSBERVNlcURhdGFTZXQoc2csIGRlc2lnbiA9IH4gbGluZV9pZCArIGNvbmRpdGlvbl9uYW1lKQpgYGAKCldlIGNhbiBhbHNvIGNyZWF0ZSBhICpERVNlcURhdGFTZXQqIGRpcmVjdGx5IGZyb20gYSBjb3VudCBtYXRyaXgsIGEgZGF0YSBmcmFtZQp3aXRoIHNhbXBsZSBpbmZvcm1hdGlvbiBhbmQgYSBkZXNpZ24gZm9ybXVsYSAoc2VlIHRoZSBgREVTZXFEYXRhU2V0RnJvbU1hdHJpeGAgCmZ1bmN0aW9uKS4gCgojIyBUaGUgKkRHRUxpc3QqIAoKQXMgbWVudGlvbmVkIGFib3ZlLCB0aGUgKmVkZ2VSKiBwYWNrYWdlIHVzZXMgYW5vdGhlciB0eXBlIG9mIGRhdGEgY29udGFpbmVyLCAKbmFtZWx5IGEgKkRHRUxpc3QqIG9iamVjdC4gKnR4aW1ldGEqIHByb3ZpZGVzIGEgY29udmVuaWVudCB3cmFwcGVyIGZ1bmN0aW9uIAp0byBnZW5lcmF0ZSBhICpER0VMaXN0KiBmcm9tIHRoZSBnZW5lLWxldmVsICpTdW1tYXJpemVkRXhwZXJpbWVudCogb2JqZWN0OiAKCmBgYHtyIGRnZWxpc3QsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZWRnZVIpCgpkZ2UgPC0gdHhpbWV0YTo6bWFrZURHRUxpc3Qoc2cpCm5hbWVzKGRnZSkKaGVhZChkZ2Ukc2FtcGxlcykKYGBgCgpBcyBmb3IgdGhlICpERVNlcURhdGFTZXQqLCBhICpER0VMaXN0KiBjYW4gYWxzbyBiZSBnZW5lcmF0ZWQgCmRpcmVjdGx5IGZyb20gYSBjb3VudCBtYXRyaXggYW5kIHNhbXBsZSBtZXRhZGF0YSAoc2VlIHRoZSBgREdFTGlzdCgpYCAKY29uc3RydWN0b3IgZnVuY3Rpb24pLgpKdXN0IGxpa2UgdGhlICpTdW1tYXJpemVkRXhwZXJpbWVudCogYW5kIHRoZSAqREVTZXFEYXRhU2V0KiwgdGhlICpER0VMaXN0KiAKY29udGFpbnMgYWxsIHRoZSBpbmZvcm1hdGlvbiB3ZSBuZWVkOiB0aGUgY291bnQgbWF0cml4LCBpbmZvcm1hdGlvbiBhYm91dCB0aGUKc2FtcGxlcyAodGhlIGNvbHVtbnMgb2YgdGhlIGNvdW50IG1hdHJpeCksIGFuZCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZ2VuZXMgKHRoZQpyb3dzIG9mIHRoZSBjb3VudCBtYXRyaXgpLiBPbmUgZGlmZmVyZW5jZSBjb21wYXJlZCB0byB0aGUgKkRFU2VxRGF0YVNldCogaXMgdGhhdAp0aGUgZXhwZXJpbWVudGFsIGRlc2lnbiBpcyBub3QgZGVmaW5lZCB3aGVuIGNyZWF0aW5nIHRoZSAqREdFTGlzdCosIGJ1dCBsYXRlciBpbgp0aGUgd29ya2Zsb3cuCgojIyMgUXVpeiB7LX0KCiogSG93IGltcG9ydGFudCBpcyB0aGUgZGVmaW5pdGlvbiBvZiB0aGUgZGVzaWduPwoqIEhvdyBkbyBJIHF1YW50aWZ5IHRoZSBlZmZlY3Qgc2l6ZT8gIkluIHdoaWNoIGRpcmVjdGlvbiIgaXMgdGhpcyB0byBiZSBpbnRlcnByZXRlZD8KKiBJcyBpdCBwb3NzaWJsZSB0byBjaGFuZ2UgdGhlIHJlZmVyZW5jZSBpbiB0aGUgY29tcGFyaXNvbj8KKiBJZiB5b3UgaGF2ZSBtdWx0aXBsZSBleHBlcmltZW50YWwgZmFjdG9ycywgaG93IHNob3VsZCB5b3UgcHJvY2VlZD8gVGhpbmsgb2YgMiBjb25kaXRpb24tMiB0aXNzdWVzL2NlbGwgdHlwZXMKCiMgRmlsdGVyaW5nCgpJdCBpcyBvZnRlbiBoZWxwZnVsIHRvIGZpbHRlciBvdXQgbG93bHkgZXhwcmVzc2VkIGdlbmVzIGJlZm9yZSBjb250aW51aW5nIAp3aXRoIHRoZSBhbmFseXNpcywgdG8gcmVtb3ZlIGZlYXR1cmVzIHRoYXQgaGF2ZSBuZWFybHkgbm8gaW5mb3JtYXRpb24sIAppbmNyZWFzZSB0aGUgc3BlZWQgb2YgdGhlIGFuYWx5c2lzIGFuZCByZWR1Y2UgdGhlIHNpemUgb2YgdGhlIGRhdGEuIApBdCB0aGUgdmVyeSBsZWFzdCB3ZSBleGNsdWRlIGdlbmVzIHdpdGggemVybyBjb3VudHMgYWNyb3NzIAphbGwgc2FtcGxlcy4gCgpgYGB7ciBmaWx0ZXJpbmcwfQpucm93KGRkcykKdGFibGUocm93U3Vtcyhhc3NheShkZHMsICJjb3VudHMiKSkgPT0gMCkKYGBgCgpIZXJlLCB3ZSBhZGRpdGlvbmFsbHkgcmVtb3ZlIGdlbmVzIHRoYXQgaGF2ZSBhIHNpbmdsZSByZWFkIGFjcm9zcyB0aGUgc2FtcGxlcy4gCgpgYGB7ciBmaWx0ZXJpbmcxfQprZWVwIDwtIHJvd1N1bXMoY291bnRzKGRkcykpID4gMQpkZHMgPC0gZGRzW2tlZXAsIF0KZGdlIDwtIGRnZVttYXRjaChyb3duYW1lcyhkZHMpLCByb3duYW1lcyhkZ2UpKSwgXQpkaW0oZGRzKQpkaW0oZGdlKQpgYGAKCkltcG9ydGFudGx5LCB0aGUgZ3JvdXAgaW5mb3JtYXRpb24gc2hvdWxkICpub3QqIGJlIHVzZWQgdG8gZGVmaW5lIHRoZSBmaWx0ZXJpbmcgCmNyaXRlcmlvbiwgc2luY2UgdGhhdCBjYW4gaW50ZXJmZXJlIHdpdGggdGhlIHZhbGlkaXR5IG9mIHRoZSBwLXZhbHVlcyBkb3duc3RyZWFtLgoKIyMjIFF1aXogey19CgoqIFJlbW92aW5nIHVuZXhwcmVzc2VkIGZlYXR1cmVzIG1pZ2h0IGluZmx1ZW5jZSB3aGF0IHlvdSBkbyB3aGVuIGludGVncmF0aW5nIGRpZmZlcmVudCBhbmFseXNlcy4gSG93IGNvdWxkIHdlIHByb2NlZWQgaW4gc3VjaCBjYXNlcz8KKiBXaGF0IGlmIEkgZG93bmxvYWQgcHVibGljbHkgYXZhaWxhYmxlIGRhdGEgYW5kIHNvbWUgZ2VuZXMgYXJlIG1pc3Npbmc/IFdoYXQgY2FuIGJlIHNvbWUgb2YgdGhlIHBvc3NpYmxlIHJlYXNvbnMgZm9yIHRoaXM/IAoKIyBFeHBsb3JhdG9yeSBhbmFseXNpcyBhbmQgdmlzdWFsaXphdGlvbgoKVGhlcmUgYXJlIHR3byBzZXBhcmF0ZSBhbmFseXNpcyBwYXRocyBpbiB0aGlzIHR1dG9yaWFsOgoKMS4gKnZpc3VhbCBleHBsb3JhdGlvbiogb2Ygc2FtcGxlIHJlbGF0aW9uc2hpcHMsIGluIHdoaWNoIHdlIHdpbGwgZGlzY3Vzcwp0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgY291bnRzIGZvciBjb21wdXRpbmcgZGlzdGFuY2VzIG9yIG1ha2luZyBwbG90cwoyLiAqc3RhdGlzdGljYWwgdGVzdGluZyogZm9yIGRpZmZlcmVuY2VzIGF0dHJpYnV0YWJsZSB0byB0cmVhdG1lbnQsIGNvbnRyb2xsaW5nCmZvciBkb25vciBlZmZlY3RzCgpJbXBvcnRhbnRseSwgdGhlIHN0YXRpc3RpY2FsIHRlc3RpbmcgbWV0aG9kcyByZWx5IG9uIG9yaWdpbmFsIGNvdW50IGRhdGEgKG5vdApzY2FsZWQgb3IgdHJhbnNmb3JtZWQpIGZvciBjYWxjdWxhdGluZyB0aGUgcHJlY2lzaW9uIG9mIG1lYXN1cmVtZW50cy4gSG93ZXZlciwKZm9yIHZpc3VhbGl6YXRpb24gYW5kIGV4cGxvcmF0b3J5IGFuYWx5c2lzLCB0cmFuc2Zvcm1lZCBjb3VudHMgYXJlIHR5cGljYWxseQptb3JlIHN1aXRhYmxlLiBUaHVzLCBpdCBpcyBjcml0aWNhbCB0byBzZXBhcmF0ZSB0aGUgdHdvIHdvcmtmbG93cyBhbmQgdXNlIHRoZSAKYXBwcm9wcmlhdGUgaW5wdXQgZGF0YSBmb3IgZWFjaCBvZiB0aGVtLgoKIyMgVHJhbnNmb3JtYXRpb25zCgpNYW55IGNvbW1vbiBzdGF0aXN0aWNhbCBtZXRob2RzIGZvciBleHBsb3JhdG9yeSBhbmFseXNpcyBvZiBtdWx0aWRpbWVuc2lvbmFsCmRhdGEsIGZvciBleGFtcGxlIGNsdXN0ZXJpbmcgYW5kICpwcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyogKFBDQSksIHdvcmsKYmVzdCBmb3IgZGF0YSB0aGF0IGdlbmVyYWxseSBoYXMgdGhlIHNhbWUgcmFuZ2Ugb2YgdmFyaWFuY2UgYXQgZGlmZmVyZW50IHJhbmdlcwpvZiB0aGUgbWVhbiB2YWx1ZXMuIFdoZW4gdGhlIGV4cGVjdGVkIGFtb3VudCBvZiB2YXJpYW5jZSBpcyBhcHByb3hpbWF0ZWx5IHRoZQpzYW1lIGFjcm9zcyBkaWZmZXJlbnQgbWVhbiB2YWx1ZXMsIHRoZSBkYXRhIGlzIHNhaWQgdG8gYmUgKmhvbW9za2VkYXN0aWMqLiBGb3IgClJOQS1zZXEgcmF3IGNvdW50cywgaG93ZXZlciwgdGhlIHZhcmlhbmNlIGdyb3dzIHdpdGggdGhlIG1lYW4uIEZvciBleGFtcGxlLCBpZgpvbmUgcGVyZm9ybXMgUENBIGRpcmVjdGx5IG9uIGEgbWF0cml4IG9mIHNpemUtZmFjdG9yLW5vcm1hbGl6ZWQgcmVhZCBjb3VudHMsIHRoZQpyZXN1bHQgdHlwaWNhbGx5IGRlcGVuZHMgb25seSBvbiB0aGUgZmV3IG1vc3Qgc3Ryb25nbHkgZXhwcmVzc2VkIGdlbmVzIGJlY2F1c2UKdGhleSBzaG93IHRoZSBsYXJnZXN0IGFic29sdXRlIGRpZmZlcmVuY2VzIGJldHdlZW4gc2FtcGxlcy4gQSBzaW1wbGUgYW5kIG9mdGVuCnVzZWQgc3RyYXRlZ3kgdG8gYXZvaWQgdGhpcyBpcyB0byB0YWtlIHRoZSBsb2dhcml0aG0gb2YgdGhlIG5vcm1hbGl6ZWQgY291bnQKdmFsdWVzIHBsdXMgYSBzbWFsbCBwc2V1ZG9jb3VudDsgaG93ZXZlciwgbm93IHRoZSBnZW5lcyB3aXRoIHRoZSB2ZXJ5IGxvd2VzdApjb3VudHMgd2lsbCB0ZW5kIHRvIGRvbWluYXRlIHRoZSByZXN1bHRzIGJlY2F1c2UsIGR1ZSB0byB0aGUgc3Ryb25nIFBvaXNzb24Kbm9pc2UgaW5oZXJlbnQgdG8gc21hbGwgY291bnQgdmFsdWVzLCBhbmQgdGhlIGZhY3QgdGhhdCB0aGUgbG9nYXJpdGhtIGFtcGxpZmllcwpkaWZmZXJlbmNlcyBmb3IgdGhlIHNtYWxsZXN0IHZhbHVlcywgdGhlc2UgbG93IGNvdW50IGdlbmVzIHdpbGwgc2hvdyB0aGUKc3Ryb25nZXN0IHJlbGF0aXZlIGRpZmZlcmVuY2VzIGJldHdlZW4gc2FtcGxlcy4KCkFzIGEgc29sdXRpb24sICpERVNlcTIqIG9mZmVycyB0cmFuc2Zvcm1hdGlvbnMgZm9yIGNvdW50IGRhdGEgdGhhdCBzdGFiaWxpemUgdGhlCnZhcmlhbmNlIGFjcm9zcyB0aGUgbWVhbjogdGhlICpyZWd1bGFyaXplZCBsb2dhcml0aG0qIChybG9nKSBhbmQgdGhlICp2YXJpYW5jZQpzdGFiaWxpemluZyB0cmFuc2Zvcm1hdGlvbiogKFZTVCkuIFRoZXNlIGhhdmUgc2xpZ2h0bHkgZGlmZmVyZW50CmltcGxlbWVudGF0aW9ucywgZGlzY3Vzc2VkIGEgYml0IGluIHRoZSAqREVTZXEyKiBwYXBlciBhbmQgaW4gdGhlIHZpZ25ldHRlLCBidXQKYSBzaW1pbGFyIGdvYWwgb2Ygc3RhYmlsaXppbmcgdGhlIHZhcmlhbmNlIGFjcm9zcyB0aGUgcmFuZ2Ugb2YgdmFsdWVzLiBCb3RoCnByb2R1Y2UgbG9nMi1saWtlIHZhbHVlcyBmb3IgaGlnaCBjb3VudHMuIEhlcmUgd2Ugd2lsbCB1c2UgdGhlIHZhcmlhbmNlCnN0YWJpbGl6aW5nIHRyYW5zZm9ybWF0aW9uIGltcGxlbWVudGVkIHdpdGggdGhlIGB2c3RgIGZ1bmN0aW9uLiAKCmBgYHtyIHZzZH0KdnNkIDwtIERFU2VxMjo6dnN0KGRkcykKYGBgCgpUaGlzIHJldHVybnMgYSAqREVTZXFUcmFuc2Zvcm0qIG9iamVjdC4uLgoKYGBge3IgY2xhc3N2c2R9CmNsYXNzKHZzZCkKYGBgCgouLi53aGljaCByZXRhaW5zIGFsbCB0aGUgY29sdW1uIG1ldGFkYXRhIHRoYXQgd2FzIGF0dGFjaGVkIHRvIHRoZQoqREVTZXFEYXRhU2V0KjoKCmBgYHtyIGhlYWR2c2R9CmhlYWQoY29sRGF0YSh2c2QpLCAzKQpgYGAKCgojIyBQQ0EgcGxvdAoKT25lIHdheSB0byB2aXN1YWxpemUgc2FtcGxlLXRvLXNhbXBsZSBkaXN0YW5jZXMgaXMgYSBwcmluY2lwYWwgY29tcG9uZW50cyAKYW5hbHlzaXMgKFBDQSkuIEluIHRoaXMgb3JkaW5hdGlvbiBtZXRob2QsIHRoZSBkYXRhIHBvaW50cyAoaGVyZSwgdGhlIHNhbXBsZXMpIAphcmUgcHJvamVjdGVkIG9udG8gdGhlIDJEIHBsYW5lIHN1Y2ggdGhhdCB0aGV5IHNwcmVhZCBvdXQgaW4gdGhlIHR3byBkaXJlY3Rpb25zIAp0aGF0IGV4cGxhaW4gbW9zdCBvZiB0aGUgZGlmZmVyZW5jZXMgKEZpZ3VyZSBiZWxvdykuIFRoZSB4LWF4aXMgKHRoZSBmaXJzdApwcmluY2lwYWwgY29tcG9uZW50LCBvciAqUEMxKikgaXMgdGhlIGRpcmVjdGlvbiB0aGF0IHNlcGFyYXRlcyB0aGUgZGF0YSBwb2ludHMKdGhlIG1vc3QgKGkuZS4sIHRoZSBkaXJlY3Rpb24gd2l0aCB0aGUgbGFyZ2VzdCB2YXJpYW5jZSkuIFRoZSB5LWF4aXMgKHRoZSBzZWNvbmQKcHJpbmNpcGFsIGNvbXBvbmVudCwgb3IgKlBDMiopIHJlcHJlc2VudHMgdGhlIGRpcmVjdGlvbiB3aXRoIGxhcmdlc3QgdmFyaWFuY2UKc3ViamVjdCB0byB0aGUgY29uc3RyYWludCB0aGF0IGl0IG11c3QgYmUgKm9ydGhvZ29uYWwqIHRvIHRoZSBmaXJzdCBkaXJlY3Rpb24uClRoZSBwZXJjZW50IG9mIHRoZSB0b3RhbCB2YXJpYW5jZSB0aGF0IGlzIGNvbnRhaW5lZCBpbiB0aGUgZGlyZWN0aW9uIGlzIHByaW50ZWQKaW4gdGhlIGF4aXMgbGFiZWwuIE5vdGUgdGhhdCB0aGVzZSBwZXJjZW50YWdlcyBkbyBub3Qgc3VtIHRvIDEwMCUsIGJlY2F1c2UgdGhlcmUKYXJlIG1vcmUgZGltZW5zaW9ucyB0aGF0IGNvbnRhaW4gdGhlIHJlbWFpbmluZyB2YXJpYW5jZSAoYWx0aG91Z2ggZWFjaCBvZiB0aGVzZQpyZW1haW5pbmcgZGltZW5zaW9ucyB3aWxsIGV4cGxhaW4gbGVzcyB0aGFuIHRoZSB0d28gdGhhdCB3ZSBzZWUpLgoKYGBge3IgcGxvdHBjYSwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NC41fQpERVNlcTI6OnBsb3RQQ0EodnNkLCBpbnRncm91cCA9ICJjb25kaXRpb25fbmFtZSIpCmBgYAoKQWRkaXRpb25hbGx5LCB0aGUgKnBjYUV4cGxvcmVyKiBwYWNrYWdlIGhhcyBzb21lIGZ1bmN0aW9uYWxpdHkgb24gdG9wIHRvIGV4cGxvcmUgZGF0YXNldHMgZnJvbSB0aGUgcG9pbnQgb2YgdmlldyBvZiBQcmluY2lwYWwgQ29tcG9uZW50cyAtIGluY2x1ZGluZyBhbHNvIGEgZnVuY3Rpb25hbCBpbnRlcnByZXRhdGlvbiBvZiBpdCB3aXRoIHRoZSBgcGNhMmdvKClgIGZ1bmN0aW9uLgoKYGBge3IgcGxvdHBjYTIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQpsaWJyYXJ5KHBjYUV4cGxvcmVyKQpwY2FwbG90KHZzZCwgaW50Z3JvdXAgPSAiY29uZGl0aW9uX25hbWUiLCBlbGxpcHNlID0gVFJVRSkKYGBgCgojIyBNRFMgcGxvdAoKQW5vdGhlciB3YXkgdG8gcmVkdWNlIGRpbWVuc2lvbmFsaXR5LCB3aGljaCBpcyBpbiBtYW55IHdheXMgc2ltaWxhciB0byBQQ0EsIGlzIAoqbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nKiAoTURTKS4gRm9yIE1EUywgd2UgZmlyc3QgaGF2ZSB0byBjYWxjdWxhdGUgYWxsCnBhaXJ3aXNlIGRpc3RhbmNlcyBiZXR3ZWVuIG91ciBvYmplY3RzIChzYW1wbGVzIGluIHRoaXMgY2FzZSksIGFuZCB0aGVuIGNyZWF0ZSBhCih0eXBpY2FsbHkpIHR3by1kaW1lbnNpb25hbCByZXByZXNlbnRhdGlvbiB3aGVyZSB0aGVzZSBwcmUtY2FsY3VsYXRlZCBkaXN0YW5jZXMKYXJlIHJlcHJlc2VudGVkIGFzIGFjY3VyYXRlbHkgYXMgcG9zc2libGUuIFRoaXMgbWVhbnMgdGhhdCBkZXBlbmRpbmcgb24gaG93IHRoZQpwYWlyd2lzZSBzYW1wbGUgZGlzdGFuY2VzIGFyZSBkZWZpbmVkLCB0aGUgdHdvLWRpbWVuc2lvbmFsIHBsb3QgY2FuIGJlIHZlcnkKZGlmZmVyZW50LCBhbmQgaXQgaXMgaW1wb3J0YW50IHRvIGNob29zZSBhIGRpc3RhbmNlIHRoYXQgaXMgc3VpdGFibGUgZm9yIHRoZQp0eXBlIG9mIGRhdGEgYXQgaGFuZC4KCiplZGdlUiogY29udGFpbnMgYSBmdW5jdGlvbiBgcGxvdE1EU2AsIHdoaWNoIG9wZXJhdGVzIG9uIGEgKkRHRUxpc3QqIG9iamVjdCBhbmQgCmdlbmVyYXRlcyBhIHR3by1kaW1lbnNpb25hbCBNRFMgcmVwcmVzZW50YXRpb24gb2YgdGhlIHNhbXBsZXMuIFRoZSBkZWZhdWx0IApkaXN0YW5jZSBiZXR3ZWVuIHR3byBzYW1wbGVzIGNhbiBiZSBpbnRlcnByZXRlZCBhcyB0aGUgInR5cGljYWwiIGxvZyBmb2xkIGNoYW5nZQpiZXR3ZWVuIHRoZSB0d28gc2FtcGxlcywgZm9yIHRoZSBnZW5lcyB0aGF0IGFyZSBtb3N0IGRpZmZlcmVudCBiZXR3ZWVuIHRoZW0gKGJ5IApkZWZhdWx0LCB0aGUgdG9wIDUwMCBnZW5lcywgYnV0IHRoaXMgY2FuIGJlIG1vZGlmaWVkKS4gV2UgZ2VuZXJhdGUgYW4gTURTIHBsb3QKZnJvbSB0aGUgKkRHRUxpc3QqIG9iamVjdCBgZGdlYCwgY29sb3JpbmcgYnkgdGhlIHRyZWF0bWVudCBhbmQgdXNpbmcgZGlmZmVyZW50CnBsb3Qgc3ltYm9scyBmb3IgZGlmZmVyZW50IGRvbm9ycy4gCgoqKk5vdGU6KiogU2luY2UgdGhlICpER0VMaXN0KiB3YXMgY3JlYXRlZCB1c2luZyB0aGUgYG1ha2VER0VMaXN0YCBmdW5jdGlvbiwgCnRoZSBhdmVyYWdlIHRyYW5zY3JpcHQgbGVuZ3RoIG9mZnNldHMgaGF2ZSBiZWVuIGluY29ycG9yYXRlZCBpbiB0aGUgb2JqZWN0IGFuZCAKd2lsbCBiZSB1c2VkIGFzIG9mZnNldHMgaW4gZG93bnN0cmVhbSBhbmFseXNpcy4gSWYgdGhpcyBpcyBub3QgdGhlIGNhc2UsIAp3ZSBuZWVkIHRvIGVzdGltYXRlIFRNTSBub3JtYWxpemF0aW9uIGZhY3RvcnMgYmVmb3JlIHBlcmZvcm1pbmcgZnVydGhlciBhbmFseXNpcy4KCmBgYHtyIHBsb3RtZHN9CiMgZGdlIDwtIGVkZ2VSOjpjYWxjTm9ybUZhY3RvcnMoZGdlKQpwbG90TURTKGRnZSwgdG9wID0gNTAwLCBsYWJlbHMgPSBOVUxMLCAKICAgICAgICBjb2wgPSBhcy5udW1lcmljKGRnZSRzYW1wbGVzJGNvbmRpdGlvbl9uYW1lKSwgCiAgICAgICAgY2V4ID0gMC41LCBnZW5lLnNlbGVjdGlvbiA9ICJjb21tb24iKQpgYGAKCiMjIyBRdWl6IHstfQoKKiBXaGF0IGlmIEkgZG8gbm90IHNlZSBhIGNsZWFyIHNlcGFyYXRpb24gaW4gbXkgUENBIHBsb3Q/IElzIGl0IHN0aWxsIG9rIHRvIHByb2NlZWQ/CiogV2hhdCBzaG91bGQgSSBkbyBpZiBJIGRldGVjdC9hbSBhd2FyZSBvZiBhIGJhdGNoIGVmZmVjdD8KKiBSYXcgZGF0YSB2cyBub3JtYWxpemVkIGRhdGEgdnMgdHJhbnNmb3JtZWQgZGF0YTogV2hpY2ggb25lIHNob3VsZCBJIHVzZSBpbiAiYWxsIHRoZSBjYXNlcyIgSSBjb3VsZCBlbmNvdW50ZXI/CiogIkkgaGF2ZSBzZWVuIHBlb3BsZSB1c2luZyB0U05FL1VNQVAgaW4gc2luZ2xlIGNlbGwgZGF0YSwgd2h5IHNob3VsZG4ndCB3ZSBkbyB0aGUgc2FtZSBoZXJlPyIgCgojIERpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzCgojIyBQZXJmb3JtaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHRlc3Rpbmcgd2l0aCAqREVTZXEyKgoKQXMgd2UgaGF2ZSBhbHJlYWR5IHNwZWNpZmllZCBhbiBleHBlcmltZW50YWwgZGVzaWduIHdoZW4gd2UgY3JlYXRlZCB0aGUgCipERVNlcURhdGFTZXQqLCB3ZSBjYW4gcnVuIHRoZSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBwaXBlbGluZSBvbiB0aGUgcmF3IApjb3VudHMgd2l0aCBhIHNpbmdsZSBjYWxsIHRvIHRoZSBmdW5jdGlvbiBgREVTZXFgLiBXZSBjYW4gYWxzbyBwbG90IHRoZQplc3RpbWF0ZWQgZGlzcGVyc2lvbnMuCgpgYGB7ciBERVNlcTJjYWxsfQpkZHMgPC0gREVTZXEyOjpERVNlcShkZHMpCkRFU2VxMjo6cGxvdERpc3BFc3RzKGRkcykKYGBgCgpUaGUgYERFU2VxYCBmdW5jdGlvbiB3aWxsIHByaW50IG91dCBhIG1lc3NhZ2UgZm9yIHRoZSB2YXJpb3VzIHN0ZXBzIGl0IHBlcmZvcm1zLiBUaGVzZQphcmUgZGVzY3JpYmVkIGluIG1vcmUgZGV0YWlsIGluIHRoZSBtYW51YWwgcGFnZSwgd2hpY2ggY2FuIGJlCmFjY2Vzc2VkIGJ5IHR5cGluZyBgP0RFU2VxYC4gQnJpZWZseSB0aGVzZSBhcmU6IHRoZSBlc3RpbWF0aW9uIG9mIHNpemUgZmFjdG9ycwooY29udHJvbGxpbmcgZm9yIGRpZmZlcmVuY2VzIGluIHRoZSBzZXF1ZW5jaW5nIGRlcHRoIG9mIHRoZSBzYW1wbGVzKSwgdGhlCmVzdGltYXRpb24gb2YgZGlzcGVyc2lvbiB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSwgYW5kIGZpdHRpbmcgYSBnZW5lcmFsaXplZCBsaW5lYXIKbW9kZWwuCgpBICpERVNlcURhdGFTZXQqIGlzIHJldHVybmVkIHRoYXQgY29udGFpbnMgYWxsIHRoZSBmaXR0ZWQgcGFyYW1ldGVycyB3aXRoaW4gaXQsCmFuZCB0aGUgZm9sbG93aW5nIHNlY3Rpb24gZGVzY3JpYmVzIGhvdyB0byBleHRyYWN0IG91dCByZXN1bHRzIHRhYmxlcyBvZgppbnRlcmVzdCBmcm9tIHRoaXMgb2JqZWN0LgoKQ2FsbGluZyB0aGUgYHJlc3VsdHNgIGZ1bmN0aW9uIHdpdGhvdXQgYW55IGFyZ3VtZW50cyB3aWxsIGV4dHJhY3QgdGhlIGVzdGltYXRlZCBsb2cyCmZvbGQgY2hhbmdlcyBhbmQgKnAqIHZhbHVlcyBmb3IgdGhlIGxhc3QgdmFyaWFibGUgaW4gdGhlIGRlc2lnbiBmb3JtdWxhLiBJZgp0aGVyZSBhcmUgbW9yZSB0aGFuIDIgbGV2ZWxzIGZvciB0aGlzIHZhcmlhYmxlLCBgcmVzdWx0c2Agd2lsbCBleHRyYWN0IHRoZQpyZXN1bHRzIHRhYmxlIGZvciBhIGNvbXBhcmlzb24gb2YgdGhlIGxhc3QgbGV2ZWwgb3ZlciB0aGUgZmlyc3QgbGV2ZWwuIFRoaXMKY29tcGFyaXNvbiBpcyBwcmludGVkIGF0IHRoZSB0b3Agb2YgdGhlIG91dHB1dDogYGNvbmRpdGlvbiBuYW1lIFNMMTM0NCB2cyBuYWl2ZWAuCk90aGVyIGNvbXBhcmlzb25zIGNhbiBiZSBwZXJmb3JtZWQgdmlhIHRoZSBgY29udHJhc3RgIGFyZ3VtZW50LiBGb3IgZXhhbXBsZSwgCndlIHdpbGwgZm9jdXMgb24gY29tcGFyaW5nIHRoZSBJRk4gZ2FtbWEgdHJlYXRtZW50IHRvIHRoZSBuYWl2ZSBncm91cC4KCmBgYHtyIGRlc2VxMnJlc3VsdHN9CiMjIERlZmF1bHQgLSBTTDEzNDQgdnMgbmFpdmUKcmVzIDwtIERFU2VxMjo6cmVzdWx0cyhkZHMpCmhlYWQocmVzKQoKIyMgV2UnbGwgaW5zdGVhZCBmb2N1cyBvbiBJRk5nYW1tYSB2cyBuYWl2ZQpyZXMgPC0gREVTZXEyOjpyZXN1bHRzKGRkcywgY29udHJhc3QgPSBjKCJjb25kaXRpb25fbmFtZSIsICJJRk5nIiwgIm5haXZlIikpCmhlYWQocmVzKQpgYGAKCkFzIGByZXNgIGlzIGEgKkRhdGFGcmFtZSogb2JqZWN0LCBpdCBjYXJyaWVzIG1ldGFkYXRhIHdpdGggaW5mb3JtYXRpb24gb24gdGhlCm1lYW5pbmcgb2YgdGhlIGNvbHVtbnM6CgpgYGB7ciBtY29sc3Jlc30KbWNvbHMocmVzLCB1c2UubmFtZXMgPSBUUlVFKQpgYGAKClRoZSBmaXJzdCBjb2x1bW4sIGBiYXNlTWVhbmAsIGlzIGEganVzdCB0aGUgYXZlcmFnZSBvZiB0aGUgbm9ybWFsaXplZCBjb3VudAp2YWx1ZXMsIGRpdmlkaW5nIGJ5IHNpemUgZmFjdG9ycywgdGFrZW4gb3ZlciBhbGwgc2FtcGxlcyBpbiB0aGUgKkRFU2VxRGF0YVNldCouIApUaGUgcmVtYWluaW5nIGZvdXIgY29sdW1ucyByZWZlciB0byBhIHNwZWNpZmljIGNvbnRyYXN0LCBuYW1lbHkgdGhlIGNvbXBhcmlzb24Kb2YgdGhlIGBJRk5nYCBsZXZlbCBvdmVyIHRoZSBgbmFpdmVgIGxldmVsIGZvciB0aGUgZmFjdG9yIHZhcmlhYmxlIApgY29uZGl0aW9uX25hbWVgLiAKClRoZSBjb2x1bW4gYGxvZzJGb2xkQ2hhbmdlYCBpcyB0aGUgZWZmZWN0IHNpemUgZXN0aW1hdGUuIEl0IHRlbGxzIHVzIGhvdyBtdWNoCnRoZSBnZW5lJ3MgZXhwcmVzc2lvbiBzZWVtcyB0byBoYXZlIGNoYW5nZWQgZHVlIHRvIGluZmVjdGlvbiB3aXRoIElGTiBnYW1tYQppbiBjb21wYXJpc29uIHRvIG5haXZlIHNhbXBsZXMuIFRoaXMgdmFsdWUgaXMgcmVwb3J0ZWQgb24gYSBsb2dhcml0aG1pYwpzY2FsZSB0byBiYXNlIDI6IGZvciBleGFtcGxlLCBhIGxvZzIgZm9sZCBjaGFuZ2Ugb2YgMS41IG1lYW5zIHRoYXQgdGhlIGdlbmUncwpleHByZXNzaW9uIGlzIGluY3JlYXNlZCBieSBhIG11bHRpcGxpY2F0aXZlIGZhY3RvciBvZiAyXjEuNS4KCk9mIGNvdXJzZSwgdGhpcyBlc3RpbWF0ZSBoYXMgYW4gdW5jZXJ0YWludHkgYXNzb2NpYXRlZCB3aXRoIGl0LCB3aGljaCBpcwphdmFpbGFibGUgaW4gdGhlIGNvbHVtbiBgbGZjU0VgLCB0aGUgc3RhbmRhcmQgZXJyb3IgZXN0aW1hdGUgZm9yIHRoZSBsb2cyIGZvbGQKY2hhbmdlIGVzdGltYXRlLiBXZSBjYW4gYWxzbyBleHByZXNzIHRoZSB1bmNlcnRhaW50eSBvZiBhIHBhcnRpY3VsYXIgZWZmZWN0CnNpemUgZXN0aW1hdGUgYXMgdGhlIHJlc3VsdCBvZiBhIHN0YXRpc3RpY2FsIHRlc3QuIFRoZSBwdXJwb3NlIG9mIGEgdGVzdCBmb3IKZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaXMgdG8gdGVzdCB3aGV0aGVyIHRoZSBkYXRhIHByb3ZpZGVzIHN1ZmZpY2llbnQgZXZpZGVuY2UKdG8gY29uY2x1ZGUgdGhhdCB0aGlzIHZhbHVlIGlzIHJlYWxseSBkaWZmZXJlbnQgZnJvbSB6ZXJvLiAqREVTZXEyKiBwZXJmb3JtcyBmb3IKZWFjaCBnZW5lIGEgKmh5cG90aGVzaXMgdGVzdCogdG8gc2VlIHdoZXRoZXIgZXZpZGVuY2UgaXMgc3VmZmljaWVudCB0byBkZWNpZGUgCmFnYWluc3QgdGhlICpudWxsIGh5cG90aGVzaXMqIHRoYXQgdGhlcmUgaXMgemVybyBlZmZlY3Qgb2YgdGhlIHRyZWF0bWVudCBvbiB0aGUKZ2VuZSBhbmQgdGhhdCB0aGUgb2JzZXJ2ZWQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRyZWF0bWVudCBhbmQgY29udHJvbCB3YXMgbWVyZWx5CmNhdXNlZCBieSBleHBlcmltZW50YWwgdmFyaWFiaWxpdHkgKGkuZS4sIHRoZSB0eXBlIG9mIHZhcmlhYmlsaXR5IHRoYXQgeW91IGNhbgpleHBlY3QgYmV0d2VlbiBkaWZmZXJlbnQgc2FtcGxlcyBpbiB0aGUgc2FtZSB0cmVhdG1lbnQgZ3JvdXApLiBBcyB1c3VhbCBpbgpzdGF0aXN0aWNzLCB0aGUgcmVzdWx0IG9mIHRoaXMgdGVzdCBpcyByZXBvcnRlZCBhcyBhICpwKiB2YWx1ZSwgYW5kIGl0IGlzIGZvdW5kCmluIHRoZSBjb2x1bW4gYHB2YWx1ZWAuIFJlbWVtYmVyIHRoYXQgYSAqcCogdmFsdWUgaW5kaWNhdGVzIHRoZSBwcm9iYWJpbGl0eSB0aGF0CmFuIGVmZmVjdCBhcyBzdHJvbmcgYXMgdGhlIG9ic2VydmVkIG9uZSwgb3IgZXZlbiBzdHJvbmdlciwgd291bGQgYmUgc2VlbiB1bmRlcgp0aGUgc2l0dWF0aW9uIGRlc2NyaWJlZCBieSB0aGUgbnVsbCBoeXBvdGhlc2lzLgoKV2UgY2FuIGFsc28gc3VtbWFyaXplIHRoZSByZXN1bHRzIHdpdGggdGhlIGZvbGxvd2luZyBsaW5lIG9mIGNvZGUsIHdoaWNoIHJlcG9ydHMKc29tZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uLCB0aGF0IHdpbGwgYmUgY292ZXJlZCBpbiBsYXRlciBzZWN0aW9ucy4KCmBgYHtyIHByZXBpc2VlfQpzdW1tYXJ5KHJlcykKaGlzdChyZXMkcHZhbHVlKQojIyBSZW1vdmUgdGhlIGdlbmVzIHRoYXQgd2VyZSBmaWx0ZXJlZCBvdXQgaW4gdGhlIGluZGVwZW5kZW50IGZpbHRlcmluZwpoaXN0KHJlcyRwdmFsdWVbIWlzLm5hKHJlcyRwYWRqKV0pCgojIyBXZSBhbHNvIGFkZCBhIGNvdXBsZSBvZiBleHRyYSBjb2x1bW5zIHRoYXQgd2lsbCBiZSB1c2VmdWwgZm9yIHRoZSBpbnRlcmFjdGl2ZQojIyB2aXN1YWxpemF0aW9uIGxhdGVyCnJvd0RhdGEoZGRzKSRsb2cxMERpc3BlcnNpb24gPC0gbG9nMTAocm93RGF0YShkZHMpJGRpc3BlcnNpb24pCgpyZXN0bXAgPC0gRGF0YUZyYW1lKHJlcykKcmVzdG1wJGxvZzEwQmFzZU1lYW4gPC0gbG9nMTAocmVzdG1wJGJhc2VNZWFuKQpyZXN0bXAkbWxvZzEwUFZhbHVlIDwtIC1sb2cxMChyZXN0bXAkcHZhbHVlKQpjb2xuYW1lcyhyZXN0bXApIDwtIHBhc3RlMCgiREVTZXEyX0lGTmdfdnNfbmFpdmVfIiwgY29sbmFtZXMocmVzdG1wKSkKcm93RGF0YShkZHMpIDwtIGNiaW5kKHJvd0RhdGEoZGRzKSwgcmVzdG1wKQpgYGAKCk5vdGUgdGhhdCB0aGVyZSBhcmUgbWFueSBnZW5lcyB3aXRoIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGR1ZSB0byBJRk4gZ2FtbWEKdHJlYXRtZW50IGF0IHRoZSBGRFIgbGV2ZWwgb2YgMTAlLiBUaGVyZSBhcmUKdHdvIHdheXMgdG8gYmUgbW9yZSBzdHJpY3QgYWJvdXQgd2hpY2ggc2V0IG9mIGdlbmVzIGFyZSBjb25zaWRlcmVkIHNpZ25pZmljYW50OgoKKiBsb3dlciB0aGUgZmFsc2UgZGlzY292ZXJ5IHJhdGUgdGhyZXNob2xkICh0aGUgdGhyZXNob2xkIG9uIGBwYWRqYCBpbiB0aGUKcmVzdWx0cyB0YWJsZSkKKiByYWlzZSB0aGUgbG9nMiBmb2xkIGNoYW5nZSB0aHJlc2hvbGQgZnJvbSAwIHVzaW5nIHRoZSBgbGZjVGhyZXNob2xkYCBhcmd1bWVudApvZiAqcmVzdWx0cyoKCklmIHdlIGxvd2VyIHRoZSBmYWxzZSBkaXNjb3ZlcnkgcmF0ZSB0aHJlc2hvbGQsIHdlIHNob3VsZCBhbHNvIHRlbGwgdGhpcyB2YWx1ZQp0byBgcmVzdWx0cygpYCwgc28gdGhhdCB0aGUgZnVuY3Rpb24gd2lsbCB1c2UgYW4gYWx0ZXJuYXRpdmUgdGhyZXNob2xkIGZvciB0aGUKb3B0aW1hbCBpbmRlcGVuZGVudCBmaWx0ZXJpbmcgc3RlcDoKCmBgYHtyIHJlczAwNX0KcmVzLjA1IDwtIHJlc3VsdHMoZGRzLCBhbHBoYSA9IDAuMDUsIAogICAgICAgICAgICAgICAgICBjb250cmFzdCA9IGMoImNvbmRpdGlvbl9uYW1lIiwgIklGTmciLCAibmFpdmUiKSkKdGFibGUocmVzLjA1JHBhZGogPCAwLjA1KQpgYGAKCklmIHdlIHdhbnQgdG8gcmFpc2UgdGhlIGxvZzIgZm9sZCBjaGFuZ2UgdGhyZXNob2xkLCBzbyB0aGF0IHdlIHRlc3QgZm9yIGdlbmVzCnRoYXQgc2hvdyBtb3JlIHN1YnN0YW50aWFsIGNoYW5nZXMgZHVlIHRvIHRyZWF0bWVudCwgd2Ugc2ltcGx5IHN1cHBseSBhIHZhbHVlIG9uCnRoZSBsb2cyIHNjYWxlLiBGb3IgZXhhbXBsZSwgYnkgc3BlY2lmeWluZyBgbGZjVGhyZXNob2xkID0gMWAsIHdlIGxvb2sgZm9yIGdlbmVzCnRoYXQgc2hvdyBzaWduaWZpY2FudCBlZmZlY3RzIG9mIHRyZWF0bWVudCBvbiBnZW5lIGNvdW50cyBtb3JlIHRoYW4gZG91Ymxpbmcgb3IKbGVzcyB0aGFuIGhhbHZpbmcsIGJlY2F1c2UgMl4xID0gMi4KCmBgYHtyIHJlc2xmY30KcmVzTEZDMSA8LSByZXN1bHRzKGRkcywgbGZjVGhyZXNob2xkID0gMSwgCiAgICAgICAgICAgICAgICAgICBjb250cmFzdCA9IGMoImNvbmRpdGlvbl9uYW1lIiwgIklGTmciLCAibmFpdmUiKSkKc3VtbWFyeShyZXNMRkMxKQp0YWJsZShyZXNMRkMxJHBhZGogPCAwLjEpCmBgYAoKU29tZXRpbWVzIGEgc3Vic2V0IG9mIHRoZSAqcCogdmFsdWVzIGluIGByZXNgIHdpbGwgYmUgYE5BYCAoIm5vdCBhdmFpbGFibGUiKS4KVGhpcyBpcyAqREVTZXEqJ3Mgd2F5IG9mIHJlcG9ydGluZyB0aGF0IGFsbCBjb3VudHMgZm9yIHRoaXMgZ2VuZSB3ZXJlIHplcm8sIGFuZApoZW5jZSBubyB0ZXN0IHdhcyBhcHBsaWVkLiBJbiBhZGRpdGlvbiwgKnAqIHZhbHVlcyBjYW4gYmUgYXNzaWduZWQgYE5BYCBpZiB0aGUKZ2VuZSB3YXMgZXhjbHVkZWQgZnJvbSBhbmFseXNpcyBiZWNhdXNlIGl0IGNvbnRhaW5lZCBhbiBleHRyZW1lIGNvdW50IG91dGxpZXIuCkZvciBtb3JlIGluZm9ybWF0aW9uLCBzZWUgdGhlIG91dGxpZXIgZGV0ZWN0aW9uIHNlY3Rpb24gb2YgdGhlICpERVNlcTIqCnZpZ25ldHRlLgoKV2l0aCAqREVTZXEyKiwgdGhlcmUgaXMgYWxzbyBhbiBlYXN5IHdheSB0byBwbG90IHRoZSAobm9ybWFsaXplZCwgdHJhbnNmb3JtZWQpCmNvdW50cyBmb3Igc3BlY2lmaWMgZ2VuZXMsIHVzaW5nIHRoZSBgcGxvdENvdW50c2AgZnVuY3Rpb246CgpgYGB7ciBwbG90Y291bnRzfQpwbG90Q291bnRzKGRkcywgZ2VuZSA9ICJFTlNHMDAwMDAxMjY1NjEuMTYiLCBpbnRncm91cCA9ICJjb25kaXRpb25fbmFtZSIsIAogICAgICAgICAgIG5vcm1hbGl6ZWQgPSBUUlVFLCB0cmFuc2Zvcm0gPSBGQUxTRSkKCkdlbmVUb25pYzo6Z2VuZV9wbG90KGRkcywgZ2VuZSA9ICJFTlNHMDAwMDAxMjY1NjEuMTYiLCBpbnRncm91cCA9ICJjb25kaXRpb25fbmFtZSIsIAogICAgICAgICAgIG5vcm1hbGl6ZWQgPSBUUlVFLCB0cmFuc2Zvcm0gPSBGQUxTRSkKYGBgCgoKIyMgUGVyZm9ybWluZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiB0ZXN0aW5nIHdpdGggKmVkZ2VSKgoKTmV4dCB3ZSB3aWxsIHNob3cgaG93IHRvIHBlcmZvcm0gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMgd2l0aCAqZWRnZVIqLgpSZWNhbGwgdGhhdCB3ZSBoYXZlIGEgKkRHRUxpc3QqIGBkZ2VgLCBjb250YWluaW5nIGFsbCB0aGUgbmVjZXNzYXJ5IGluZm9ybWF0aW9uOgoKYGBge3IgbmFtZXNkZ2V9Cm5hbWVzKGRnZSkKYGBgCgpXZSBmaXJzdCBkZWZpbmUgYSBkZXNpZ24gbWF0cml4LCB1c2luZyB0aGUgc2FtZSBmb3JtdWxhIHN5bnRheCBhcyBmb3IgKkRFU2VxMioKYWJvdmUuCgpgYGB7ciBkZXNpZ25lZGdlcn0KZGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+IGxpbmVfaWQgKyBjb25kaXRpb25fbmFtZSwgZGF0YSA9IGRnZSRzYW1wbGVzKQpoZWFkKGRlc2lnbikKYGBgCgpXaGlsZSAqREVTZXEyKiBwZXJmb3JtcyBpbmRlcGVuZGVudCBmaWx0ZXJpbmcgb2YgbG93bHkgZXhwcmVzc2VkIGdlbmVzCmludGVybmFsbHksIHRoaXMgaXMgZG9uZSBieSB0aGUgdXNlciBiZWZvcmUgYXBwbHlpbmcgKmVkZ2VSKi4gSGVyZSwgd2UgZmlsdGVyCm91dCBsb3dseSBleHByZXNzZWQgZ2VuZXMgdXNpbmcgdGhlIGBmaWx0ZXJCeUV4cHIoKWAgZnVuY3Rpb24sIGFuZCB0aGVuIGVzdGltYXRlCnRoZSBkaXNwZXJzaW9uIGZvciBlYWNoIGdlbmUuIE5vdGUgdGhhdCBpdCBpcyBpbXBvcnRhbnQgdGhhdCB3ZSBzcGVjaWZ5IHRoZQpkZXNpZ24gaW4gdGhlIGRpc3BlcnNpb24gY2FsY3VsYXRpb24gKGl0IHdpbGwgYmUgdXNlZCB0byBkZXRlcm1pbmUgYSAKc3VpdGFibGUgbnVtYmVyIG9mIHNhbXBsZXMgdG8gcmVxdWlyZSBhIGdlbmUgdG8gYmUgZXhwcmVzc2VkIGluKS4gCkFmdGVyd2FyZHMsIHdlIHBsb3QgdGhlIGVzdGltYXRlZCBkaXNwZXJzaW9ucy4KCmBgYHtyIGZpbHRlcn0Ka2VlcCA8LSBlZGdlUjo6ZmlsdGVyQnlFeHByKGRnZSwgZGVzaWduKQpkZ2UgPC0gZGdlW2tlZXAsIF0KZGdlIDwtIGVkZ2VSOjplc3RpbWF0ZURpc3AoZGdlLCBkZXNpZ24pCmVkZ2VSOjpwbG90QkNWKGRnZSkKYGBgCgpGaW5hbGx5LCB3ZSBmaXQgdGhlIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbCBhbmQgcGVyZm9ybSB0aGUgdGVzdC4gSW4gdGhlCmBnbG1RTEZUZXN0YCBmdW5jdGlvbiwgd2UgaW5kaWNhdGUgd2hpY2ggY29lZmZpY2llbnQgKHdoaWNoIGNvbHVtbiBpbiB0aGUgZGVzaWduCm1hdHJpeCkgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIHRlc3QgZm9yLiBJdCBpcyBwb3NzaWJsZSB0byB0ZXN0IG1vcmUgZ2VuZXJhbCAKY29udHJhc3RzIGFzIHdlbGwsIGFuZCB0aGUgdXNlciBndWlkZSBjb250YWlucyBtYW55IGV4YW1wbGVzIG9uIGhvdyB0byBkbyB0aGlzLgpUaGUgYHRvcFRhZ3NgIGZ1bmN0aW9uIGV4dHJhY3RzIHRoZSB0b3AtcmFua2VkIGdlbmVzLiBZb3UgY2FuIGluZGljYXRlIHRoZQphZGp1c3RlZCBwLXZhbHVlIGN1dG9mZiwgYW5kL29yIHRoZSBudW1iZXIgb2YgZ2VuZXMgdG8ga2VlcC4KCmBgYHtyIGVkZ2VSY2FsbH0KZml0IDwtIGVkZ2VSOjpnbG1RTEZpdChkZ2UsIGRlc2lnbikKcWxmIDwtIGVkZ2VSOjpnbG1RTEZUZXN0KGZpdCwgY29lZiA9ICJjb25kaXRpb25fbmFtZUlGTmciKQp0dC5hbGwgPC0gZWRnZVI6OnRvcFRhZ3MocWxmLCBuID0gbnJvdyhkZ2UpLCBzb3J0LmJ5ID0gIm5vbmUiKSAjIGFsbCBnZW5lcwpoaXN0KHR0LmFsbCR0YWJsZSRQVmFsdWUpCnR0IDwtIGVkZ2VSOjp0b3BUYWdzKHFsZiwgbiA9IG5yb3coZGdlKSwgcC52YWx1ZSA9IDAuMSkgIyBnZW5lcyB3aXRoIGFkai5wPDAuMQp0dDEwIDwtIGVkZ2VSOjp0b3BUYWdzKHFsZikgIyBqdXN0IHRoZSB0b3AgMTAgYnkgZGVmYXVsdAp0dDEwCmBgYAoKVGhlIGNvbHVtbnMgaW4gdGhlICplZGdlUiogcmVzdWx0IGRhdGEgZnJhbWUgYXJlIHNpbWlsYXIgdG8gdGhlIG9uZXMgb3V0cHV0IGJ5CipERVNlcTIqLiAqZWRnZVIqIHJlcHJlc2VudHMgdGhlIG92ZXJhbGwgZXhwcmVzc2lvbiBsZXZlbCBvbiB0aGUgbG9nLUNQTSBzY2FsZQpyYXRoZXIgdGhhbiBvbiB0aGUgbm9ybWFsaXplZCBjb3VudCBzY2FsZSB0aGF0ICpERVNlcTIqIHVzZXMuIFRoZSBgRmAgY29sdW1uCmNvbnRhaW5zIHRoZSB0ZXN0IHN0YXRpc3RpYywgYW5kIHRoZSBgRkRSYCBjb2x1bW4gY29udGFpbnMgdGhlCkJlbmphbWluaS1Ib2NoYmVyZyBhZGp1c3RlZCBwLXZhbHVlcy4KCldlIGNhbiBjb21wYXJlIHRoZSBzZXRzIG9mIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIHRvIHNlZQpob3cgdGhlIHJlc3VsdHMgZnJvbSB0aGUgdHdvIHBhY2thZ2VzIG92ZXJsYXA6CgpgYGB7ciBkZXNlcTJ2c2VkZ2VyfQpzaGFyZWQgPC0gaW50ZXJzZWN0KHJvd25hbWVzKHJlcyksIHJvd25hbWVzKHR0LmFsbCR0YWJsZSkpCnRhYmxlKERFU2VxMiA9IHJlcyRwYWRqW21hdGNoKHNoYXJlZCwgcm93bmFtZXMocmVzKSldIDwgMC4xLCAKICAgICAgZWRnZVIgPSB0dC5hbGwkdGFibGUkRkRSW21hdGNoKHNoYXJlZCwgcm93bmFtZXModHQuYWxsJHRhYmxlKSldIDwgMC4xKQpgYGAKCldlIGNhbiBhbHNvIGNvbXBhcmUgdGhlIHR3byByZXN1bHQgbGlzdHMgYnkgdGhlIHJhbmtzOgoKYGBge3IgZGVzZXEydnNlZGdlcnBsb3R9CnBsb3QocmFuayhyZXMkcHZhbHVlW21hdGNoKHNoYXJlZCwgcm93bmFtZXMocmVzKSldKSwgCiAgICAgcmFuayh0dC5hbGwkdGFibGUkUFZhbHVlW21hdGNoKHNoYXJlZCwgcm93bmFtZXModHQuYWxsJHRhYmxlKSldKSwgCiAgICAgY2V4ID0gMC4xLCB4bGFiID0gIkRFU2VxMiIsIHlsYWIgPSAiZWRnZVIiKQpgYGAKCkFsc28gd2l0aCAqZWRnZVIqIHdlIGNhbiB0ZXN0IGZvciBzaWduaWZpY2FuY2UgcmVsYXRpdmUgdG8gYSBmb2xkLWNoYW5nZQp0aHJlc2hvbGQsIHVzaW5nIHRoZSBmdW5jdGlvbiBgZ2xtVHJlYXRgLiBCZWxvdyB3ZSBzZXQgdGhlIGxvZyBmb2xkLWNoYW5nZQp0aHJlc2hvbGQgdG8gMSAoaS5lLiwgZm9sZCBjaGFuZ2UgdGhyZXNob2xkIGVxdWFsIHRvIDIpLCBhcyBmb3IgKkRFU2VxMiogYWJvdmUuCgpgYGB7ciB0cmVhdH0KdHJlYXRyZXMgPC0gZWRnZVI6OmdsbVRyZWF0KGZpdCwgY29lZiA9ICJjb25kaXRpb25fbmFtZUlGTmciLCBsZmMgPSAxKQp0dC50cmVhdCA8LSBlZGdlUjo6dG9wVGFncyh0cmVhdHJlcywgbiA9IG5yb3coZGdlKSwgc29ydC5ieSA9ICJub25lIikKYGBgCgoKCiMjIyBRdWl6IHstfQoKKiBBIGZlYXR1cmUgYmVpbmcgY2FsbGVkIERFOiAiV2h5IGlzIG15IGV4cGVjdGVkIHNob3J0bGlzdGVkIGdlbmUgbm90IHNob3dpbmcgdXA/IiBXaGF0IHBvc3NpYmxlIGZhY3RvcnMgY2FuIHBsYXkgYSByb2xlIGluIHRoZSBmZWF0dXJlIGJlaW5nIGFib3ZlIG9yIGJlbG93IHRoZSBzaWduaWZpY2FuY2UgdGhyZXNob2xkPwoqIFNoYWxsIEkgc3Vic2V0IG15IERFIHJlc3VsdHMgdG8gdGhlIGdlbmVzIG9ubHkgZGV0ZWN0ZWQgYXMgREU/IFdoeT8KKiBUaGUgcmVzdWx0cyBiZXR3ZWVuIERFU2VxMiBhbmQgZWRnZVIgbWlnaHQgZGlmZmVyLiBXaGljaCBvbmUgaXMgImNvcnJlY3QiPyBIb3cgY2FuIHlvdSBiZXR0ZXIgYXBwcmVjaWF0ZSB0aGUgZXhpc3RpbmcgY29tbW9uYWxpdGllcyBhbmQgZGlmZmVyZW5jZXMgaW4gdGhlIERFIGFuYWx5c2lzPwoqIFRoZSB0aG91Z2h0IGFib3ZlIGlzIHZhbGlkIGFsc28gZm9yIHRoZSBwcm9jZXNzIG9mIGludGVncmF0aW5nIGRpZmZlcmVudCBERSBhbmFseXNlcyAoaS5lLiB1c2luZyB0aGUgc2FtZSBtZXRob2QsIGJ1dCAiY29tcGFyaW5nIGRpZmZlcmVudCBncm91cHMiKS4gV2hhdCBjYW4geW91IHRoaW5rIG9mIHRvIHJlcHJlc2VudCB0aGVzZSByZXN1bHRzPwoKIyBQbG90dGluZyByZXN1bHRzCgojIyBNQSBwbG90IHdpdGggREVTZXEyCgpBbiAqTUEtcGxvdCogW0BEdWRvaXQyMDAyU3RhdGlzdGljYWxdIHByb3ZpZGVzIGEgdXNlZnVsIG92ZXJ2aWV3IGZvciBhbiAKZXhwZXJpbWVudCB3aXRoIGEgdHdvLWdyb3VwIGNvbXBhcmlzb24uIFRoZSBsb2cyIGZvbGQgY2hhbmdlIGZvcgphIHBhcnRpY3VsYXIgY29tcGFyaXNvbiBpcyBwbG90dGVkIG9uIHRoZSB5LWF4aXMgYW5kIHRoZSBhdmVyYWdlIG9mIHRoZSBjb3VudHMKbm9ybWFsaXplZCBieSBzaXplIGZhY3RvciBpcyBzaG93biBvbiB0aGUgeC1heGlzICgiTSIgZm9yIG1pbnVzLCBiZWNhdXNlIGEgbG9nCnJhdGlvIGlzIGVxdWFsIHRvIGxvZyBtaW51cyBsb2csIGFuZCAiQSIgZm9yIGF2ZXJhZ2UpLiBFYWNoIGdlbmUgaXMgcmVwcmVzZW50ZWQKd2l0aCBhIGRvdC4gR2VuZXMgd2l0aCBhbiBhZGp1c3RlZCAqcCogdmFsdWUgYmVsb3cgYSB0aHJlc2hvbGQgKGhlcmUgMC4xLCB0aGUKZGVmYXVsdCB3aXRoICpERVNlcTIqKSBhcmUgc2hvd24gaW4gY29sb3IKCmBgYHtyIHBsb3RtYX0KREVTZXEyOjpwbG90TUEocmVzLCB5bGltID0gYygtNSwgNSkpCmBgYAoKV2Ugc2VlIHRoYXQgdGhlcmUgYXJlIG1hbnkgZ2VuZXMgd2l0aCBsb3cgZXhwcmVzc2lvbiBsZXZlbHMgdGhhdCBuZXZlcnRoZWxlc3MgCmhhdmUgbGFyZ2UgZm9sZCBjaGFuZ2VzIChzaW5jZSB3ZSBhcmUsIGVmZmVjdGl2ZWx5LCBkaXZpZGluZyBieSBhIHNtYWxsIApudW1iZXIpLiBUbyBnZXQgbW9yZSBpbnRlcnByZXRhYmxlIGxvZyBmb2xkIGNoYW5nZXMgKGUuZy4sIGZvciByYW5raW5nIGdlbmVzKSwgCndlIHVzZSB0aGUgYGxmY1Nocmlua2AgZnVuY3Rpb24gdG8gc2hyaW5rIHRoZSBsb2cyCmZvbGQgY2hhbmdlcyBmb3IgdGhlIGNvbXBhcmlzb24gb2YgSUZOIGdhbW1hLXRyZWF0ZWQgdnMgbmFpdmUgc2FtcGxlcy4gVGhlcmUgYXJlCnRocmVlIHR5cGVzIG9mIHNocmlua2FnZSBlc3RpbWF0b3JzIGluICpERVNlcTIqLCB3aGljaCBhcmUgY292ZXJlZCBpbiB0aGUKdmlnbmV0dGUuIEhlcmUgd2Ugc3BlY2lmeSB0aGUgX2FwZWdsbV8gbWV0aG9kIGZvciBzaHJpbmtpbmcgY29lZmZpY2llbnRzLCB3aGljaAppcyBnb29kIGZvciBzaHJpbmtpbmcgdGhlIG5vaXN5IExGQyBlc3RpbWF0ZXMgd2hpbGUgZ2l2aW5nIGxvdyBiaWFzIExGQwplc3RpbWF0ZXMgZm9yIHRydWUgbGFyZ2UgZGlmZmVyZW5jZXMgW0BaaHUyMDE5LWFwZWdsbV0uIFRvIHVzZSBhcGVnbG0gd2Ugc3BlY2lmeQphIGNvZWZmaWNpZW50IGZyb20gdGhlIG1vZGVsIHRvIHNocmluaywgZWl0aGVyIGJ5IG5hbWUgb3IgbnVtYmVyIGFzIHRoZQpjb2VmZmljaWVudCBhcHBlYXJzIGluIGByZXN1bHRzTmFtZXMoZGRzKWAuCgpgYGB7ciBhcGVnbG19CmxpYnJhcnkoYXBlZ2xtKQpERVNlcTI6OnJlc3VsdHNOYW1lcyhkZHMpCnJlc2FwZSA8LSBERVNlcTI6OmxmY1NocmluayhkZHMsIGNvZWYgPSAiY29uZGl0aW9uX25hbWVfSUZOZ192c19uYWl2ZSIsIHR5cGUgPSAiYXBlZ2xtIikKREVTZXEyOjpwbG90TUEocmVzYXBlLCB5bGltID0gYygtNSwgNSkpCmBgYAoKCiMjIE1BIC8gU21lYXIgcGxvdCB3aXRoIGVkZ2VSCgpJbiAqZWRnZVIqLCB0aGUgTUEgcGxvdCBpcyBvYnRhaW5lZCB2aWEgdGhlIGBwbG90U21lYXJgIGZ1bmN0aW9uLiAKCmBgYHtyIHNtZWFyfQplZGdlUjo6cGxvdFNtZWFyKHFsZiwgZGUudGFncyA9IHJvd25hbWVzKHR0JHRhYmxlKSkKYGBgCgojIyBIZWF0bWFwIG9mIHRoZSBtb3N0IHNpZ25pZmljYW50IGdlbmVzCgpBbm90aGVyIHdheSBvZiByZXByZXNlbnRpbmcgdGhlIHJlc3VsdHMgb2YgYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBpcwp0byBjb25zdHJ1Y3QgYSBoZWF0bWFwIG9mIHRoZSB0b3AgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLiAKQSBoZWF0bWFwIGlzIGEgImNvbG9yIGNvZGVkIGV4cHJlc3Npb24gbWF0cml4Iiwgd2hlcmUgdGhlIHJvd3MgYW5kIGNvbHVtbnMgYXJlIApjbHVzdGVyZWQgdXNpbmcgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcuIFR5cGljYWxseSwgaXQgc2hvdWxkIG5vdCBiZSBhcHBsaWVkIHRvIApjb3VudHMsIGJ1dCB3b3JrcyBiZXR0ZXIgd2l0aCB0cmFuc2Zvcm1lZCB2YWx1ZXMuIEhlcmUgd2Ugc2hvdyBob3cgaXQgY2FuIGJlIAphcHBsaWVkIHRvIHRoZSB2YXJpYW5jZS1zdGFiaWxpemVkIHZhbHVlcyBnZW5lcmF0ZWQgYWJvdmUuIFdlIHdvdWxkCmV4cGVjdCB0aGUgY29udHJhc3RlZCBzYW1wbGUgZ3JvdXBzIHRvIGNsdXN0ZXIgc2VwYXJhdGVseSAoImJ5IGNvbnN0cnVjdGlvbiIsIApzaW5jZSB0aGUgZ2VuZXMgd2VyZSBzZWxlY3RlZCB0byBiZSBtb3N0IGRpc2NyaW1pbmF0aXZlIGJldHdlZW4gdGhlIGdyb3VwcykuIApUaGUgaGVhdG1hcCB3aWxsIGFsbG93IHVzIHRvIGRpc3BsYXksIGUuZy4sIHRoZSB2YXJpYWJpbGl0eSB3aXRoaW4gdGhlIGdyb3VwcyAKb2YgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcy4gCldlIGNob29zZSB0aGUgdG9wIDMwIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcy4gVGhlcmUgYXJlIG1hbnkgZnVuY3Rpb25zIAppbiBSIHRoYXQgY2FuIGdlbmVyYXRlIGhlYXRtYXBzLCBoZXJlIHdlIHNob3cgdGhlIG9uZSBmcm9tIHRoZSAKYHIgQ1JBTnBrZygicGhlYXRtYXAiKWAgcGFja2FnZS4KCmBgYHtyIGhlYXRtYXAsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTAsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkocGhlYXRtYXApCnN0b3BpZm5vdChyb3duYW1lcyh2c2QpID09IHJvd25hbWVzKHJlcykpCm1hdCA8LSBhc3NheSh2c2QpCnJvd25hbWVzKG1hdCkgPC0gcm93RGF0YSh2c2QpJFNZTUJPTAptYXQgPC0gbWF0W2hlYWQob3JkZXIocmVzJHBhZGopLCAzMCksIF0KbWF0IDwtIG1hdCAtIHJvd01lYW5zKG1hdCkKZGYgPC0gYXMuZGF0YS5mcmFtZShjb2xEYXRhKHZzZClbLCBjKCJjb25kaXRpb25fbmFtZSIpLCBkcm9wID0gRkFMU0VdKQpwaGVhdG1hcChtYXQsIGFubm90YXRpb25fY29sID0gZGYpCmBgYAoKV2UgY2FuIG9mIGNvdXJzZSBhbHNvIGNyZWF0ZSBoZWF0bWFwcyBmb3Igb3RoZXIgc2V0cyBvZiBnZW5lcyAtIGZvciBleGFtcGxlLCAKdGhlIGNvbGxlY3Rpb24gb2YgZ2VuZXMgd2l0aCB0aGUgaGlnaGVzdCBvdmVyYWxsIHZhcmlhbmNlICh3aGljaCBtYXkgb3IgbWF5IApub3QgaW5kaWNhdGUgYSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGdyb3VwcyAtIGluIHRoaXMgcGFydGljdWxhciBjYXNlIG1vc3QgCm9mIHRoZSBoaWdobHkgdmFyaWFibGUgZ2VuZXMgc2hvdyBhIGNsZWFyIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgZ3JvdXBzKS4KCmBgYHtyIGhlYXRtYXAyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEwLCBtZXNzYWdlPUZBTFNFfQptYXQgPC0gYXNzYXkodnNkKQpyb3duYW1lcyhtYXQpIDwtIHJvd0RhdGEodnNkKSRTWU1CT0wKCnRvcFZhckdlbmVzIDwtIGhlYWQob3JkZXIocm93VmFycyhtYXQpLCBkZWNyZWFzaW5nID0gVFJVRSksIDMwKQoKbWF0IDwtIG1hdFt0b3BWYXJHZW5lcywgXQptYXQgPC0gbWF0IC0gcm93TWVhbnMobWF0KQpkZiA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEodnNkKVssIGMoImNvbmRpdGlvbl9uYW1lIiksIGRyb3AgPSBGQUxTRV0pCnBoZWF0bWFwKG1hdCwgYW5ub3RhdGlvbl9jb2wgPSBkZikKYGBgCgpUT0RPOiBzZWUgdGhlICJzdHJpcGVzIiBpbiBzb21lIGdlbmVzIG92ZXIgYWxsIHRoZSBzYW1wbGVzCgojIyBJbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9uIHdpdGggaVNFRQoKYHIgQmlvY3BrZygiaVNFRSIpYCBpcyBhIEJpb2NvbmR1Y3RvciBwYWNrYWdlIHRoYXQgYWxsb3dzIGludGVyYWN0aXZlIGV4cGxvcmF0aW9uIG9mIGFueSBkYXRhCnN0b3JlZCBpbiBhICpTdW1tYXJpemVkRXhwZXJpbWVudCogY29udGFpbmVyLCBvciBhbnkgY2xhc3MgZXh0ZW5kaW5nIHRoaXMgKHN1Y2gKYXMsIGUuZy4sIHRoZSAqREVTZXFEYXRhU2V0KiBjbGFzcywgb3IgdGhlICpTaW5nbGVDZWxsRXhwZXJpbWVudCogZm9yCnNpbmdsZS1jZWxsIGRhdGEpLiBCeSBjYWxsaW5nIHRoZSBgaVNFRSgpYCBmdW5jdGlvbiB3aXRoIHRoZSBvYmplY3QgYXMgdGhlIGZpcnN0CmFyZ3VtZW50LCBhbiBpbnRlcmFjdGl2ZSBhcHBsaWNhdGlvbiB3aWxsIGJlIG9wZW5lZCwgaW4gd2hpY2ggYWxsIG9ic2VydmVkCnZhbHVlcyBhcyB3ZWxsIGFzIG1ldGFkYXRhIGNvbHVtbnMgKGByb3dEYXRhYCBhbmQgYGNvbERhdGFgKSBjYW4gYmUgZXhwbG9yZWQuCgpgYGB7ciBpc2VlLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KbGlicmFyeShpU0VFKQpsaWJyYXJ5KGlTRUV1KQpkZHMgPC0gaVNFRXU6OnJlZ2lzdGVyQXZlQWJQYXR0ZXJucyhkZHMsICJsb2cxMEJhc2VNZWFuIikKZGRzIDwtIGlTRUV1OjpyZWdpc3RlckxvZ0ZDUGF0dGVybnMoZGRzLCAibG9nMkZvbGRDaGFuZ2UiKQpkZHMgPC0gaVNFRXU6OnJlZ2lzdGVyUFZhbHVlUGF0dGVybnMoZGRzLCAicHZhbHVlIikKYXBwIDwtIGlTRUUoZGRzLCBpbml0aWFsID0gbGlzdChNQVBsb3QoKSwgVm9sY2Fub1Bsb3QoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93RGF0YVRhYmxlKCksIEZlYXR1cmVBc3NheVBsb3QoKSkpCiMjIHNoaW55OjpydW5BcHAoYXBwKQpgYGAKCiMjIEV4cG9ydGluZyByZXN1bHRzIHRvIENTViBmaWxlCgpZb3UgY2FuIGVhc2lseSBzYXZlIHRoZSByZXN1bHRzIHRhYmxlIGluIGEgQ1NWIGZpbGUgdGhhdCB5b3UgY2FuIHRoZW4gc2hhcmUgb3IgCmxvYWQgd2l0aCBhIHNwcmVhZHNoZWV0IHByb2dyYW0gc3VjaCBhcyBFeGNlbCAobm90ZSwgaG93ZXZlciwgdGhhdCBFeGNlbCAKc29tZXRpbWVzIGRvZXMgZnVubnkgdGhpbmdzIHRvIGdlbmUgaWRlbnRpZmllcnMgW0BaZWViZXJnMjAwNEV4Y2VsOwpAWmllbWFubjIwMTZFeGNlbF0pLiBUaGUgY2FsbCB0byAqYXMuZGF0YS5mcmFtZSogaXMgbmVjZXNzYXJ5IHRvIGNvbnZlcnQgdGhlCipEYXRhRnJhbWUqIG9iamVjdCB0byBhICpkYXRhLmZyYW1lKiBvYmplY3QKdGhhdCBjYW4gYmUgcHJvY2Vzc2VkIGJ5IGB3cml0ZS5jc3ZgLiBIZXJlLCB3ZSBmaXJzdCBzaG93IGhvdyB0byBhZGQgZ2VuZQpzeW1ib2xzIHRvIHRoZSBvdXRwdXQgdGFibGUsIGFuZCB0aGVuIGV4cG9ydCBqdXN0IHRoZSB0b3AgMTAwIGdlbmVzIGZvcgpkZW1vbnN0cmF0aW9uLgoKYGBge3IgZXhwb3J0Y3N2fQpzdG9waWZub3QoYWxsKHJvd25hbWVzKHJlcykgPT0gcm93bmFtZXMoZGRzKSkpCnJlcyRzeW1ib2wgPC0gcm93RGF0YShkZHMpJFNZTUJPTAoKcmVzT3JkZXJlZCA8LSByZXNbb3JkZXIocmVzJHBhZGopLCBdCmhlYWQocmVzT3JkZXJlZCkKCnJlc09yZGVyZWRERiA8LSBhcy5kYXRhLmZyYW1lKHJlc09yZGVyZWQpW3NlcV9sZW4oMTAwKSwgXQp3cml0ZS50YWJsZShjYmluZChpZCA9IHJvd25hbWVzKHJlc09yZGVyZWRERiksIHJlc09yZGVyZWRERiksIAogICAgICAgICAgICBmaWxlID0gInJlc3VsdHMudHh0IiwgcXVvdGUgPSBGQUxTRSwgc2VwID0gIlx0IiwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKCiMjIyBRdWl6IHstfQoKKiBJcyB2aXN1YWxpemF0aW9uIG9mIGRhdGEgYW5kIHJlc3VsdHMgKnJlYWxseSB0aGF0IGltcG9ydGFudCo/CiogVm9sY2FubyBwbG90IG9yIE1BIHBsb3QsIHdoYXQgaXMgImJldHRlciI/IFRoaW5rIG9mIHRoZSBpbmZvcm1hdGlvbiBldmVyeSBwbG90IHR5cGUgZGlzcGxheXMKKiBDYW4gSSBhbHdheXMgcGxvdCBleHByZXNzaW9uIHZhbHVlcyBmb3IgYSBzaW5nbGUgZ2VuZSwgb3IgZm9yIGEgc2V0IG9mIGdlbmVzIChpLmUuIGluZGVwZW5kZW50bHkgb2YgdGhlaXIgREUgc3RhdHVzKT8KKiBXaGF0IGNhbiBJIGRvIGlmIEkgd2FudCB0byBrbm93IG1vcmUgb24gb25lICJub3ZlbCIgZ2VuZSwgcG9zc2libHkgcmVsZXZhbnQgd2l0aCBteSBleHBlcmltZW50PwoKIyBGdW5jdGlvbmFsIGFuYWx5c2lzCgpJbiBvcmRlciB0byBpbnRlcnByZXQgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHJlc3VsdHMgaW4gdGVybXMgCm9mIGtub3duIGdlbmUgc2V0cywgd2UgY2FuIGFsc28gYXBwbHkgYSBmdW5jdGlvbmFsIGVucmljaG1lbnQgdGVzdC4gClRoZXJlIGFyZSBtYW55IGFsdGVybmF0aXZlcyB0byBwZXJmb3JtIGVucmljaG1lbnQgYW5hbHlzaXMgaW4gdGhlIGNvbnRleHQgb2YgUiBhbmQgQmlvY29uZHVjdG9yLgpBbW9uZyB0aGVzZSwgKnRvcEdPKiBhbmQgKmNsdXN0ZXJQcm9maWxlciogYXJlIHR3byBwb3B1bGFyIG9wdGlvbnMuCgpXZSBjYW4gcGVyZm9ybSB0aGUgYW5hbHlzaXMgYnkgZm9sbG93aW5nIHRoZSBpbnN0cnVjdGlvbnMgaW4gdGhlIG5leHQgY2h1bmsuCkluIGVhY2ggY2FzZSwgdGhlIGdlbmUgc2V0cyB0aGF0IGFyZSB0ZXN0ZWQgZm9yIGVucmljaG1lbnQgYXJlIG9idGFpbmVkIGZyb20gdGhlIFtHZW5lIE9udG9sb2d5XShodHRwOi8vZ2VuZW9udG9sb2d5Lm9yZy8pICdiaW9sb2dpY2FsIHByb2Nlc3MnIGNhdGFsb2cuIAoKYGBge3IgcnVuLWZ1bmN0aW9uYWxlbnJpY2htZW50fQpsaWJyYXJ5KEdlbmVUb25pYykKCnJlcyRzeW1ib2wgPC0gcm93RGF0YShkZHMpJFNZTUJPTAoKZGVfc3ltYm9sc19JRk5nX3ZzX25haXZlIDwtIGRlc2VxcmVzdWx0MmRmKHJlcywgRkRSID0gMC4wNSkkc3ltYm9sCmJnX2lkcyA8LSByb3dEYXRhKGRkcykkU1lNQk9MW3Jvd1N1bXMoY291bnRzKGRkcykpID4gMF0KCmxpYnJhcnkodG9wR08pCnRvcGdvX0RFX21hY3JvcGhhZ2VfSUZOZ192c19uYWl2ZSA8LSBwY2FFeHBsb3Jlcjo6dG9wR090YWJsZSgKICAgIERFZ2VuZXMgPSBkZV9zeW1ib2xzX0lGTmdfdnNfbmFpdmUsCiAgICBCR2dlbmVzID0gYmdfaWRzLAogICAgb250b2xvZ3kgPSAiQlAiLAogICAgbWFwcGluZyA9ICJvcmcuSHMuZWcuZGIiLAogICAgZ2VuZUlEID0gInN5bWJvbCIsCiAgICB0b3BUYWJsZXJvd3MgPSA1MDAKKQoKbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCmNsdXByb19ERV9tYWNyb3BoYWdlX0lGTmdfdnNfbmFpdmUgPC0gY2x1c3RlclByb2ZpbGVyOjplbnJpY2hHTygKICAgIGdlbmUgPSBkZV9zeW1ib2xzX0lGTmdfdnNfbmFpdmUsCiAgICB1bml2ZXJzZSA9IGJnX2lkcywKICAgIGtleVR5cGUgPSAiU1lNQk9MIiwKICAgIE9yZ0RiID0gb3JnLkhzLmVnLmRiLAogICAgb250ID0gIkJQIiwKICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgcHZhbHVlQ3V0b2ZmID0gMC4wMSwKICAgIHF2YWx1ZUN1dG9mZiA9IDAuMDUsCiAgICByZWFkYWJsZSA9IEZBTFNFCikKYGBgCgpgYGB7ciBjb250ZW50LWZ1bmNlbnJ9CkRUOjpkYXRhdGFibGUodG9wZ29fREVfbWFjcm9waGFnZV9JRk5nX3ZzX25haXZlWzE6MTAsXSkKRFQ6OmRhdGF0YWJsZShhcy5kYXRhLmZyYW1lKGNsdXByb19ERV9tYWNyb3BoYWdlX0lGTmdfdnNfbmFpdmUpWzE6MTAsXSkKYGBgCgoKIyMgU3RyZWFtbGluaW5nIGludGVycHJldGF0aW9uIG9mIHJlc3VsdHMgd2l0aCBHZW5lVG9uaWMKCiJTaW5jZSBpdCBpcyBiaW9pbmZvcm1hdGljcyIsIGV2ZXJ5IHNvZnR3YXJlIHBhY2thZ2UgY2FuIGJlIGV4cGVjdGVkIHRvIHJldHVybiBhIChzbGlnaHRseSkgZGlmZmVyZW50LWJ1dC1zaW1pbGFyLWluLWNvbnRlbnQgb3V0cHV0IGZvcm1hdC4KVG8gc2ltcGxpZnkgdGhlIGludGVycHJldGF0aW9uIG9mIHRyYW5zY3JpcHRvbWUgZGF0YXNldHMsIHRoZSAqR2VuZVRvbmljKiBwYWNrYWdlIG9mZmVycyBhbiBpbnRlcmFjdGl2ZSBhcHBsaWNhdGlvbiB0byBleHBsb3JlIGluIGRlcHRoIGFsbCB0aGUgd29ya2Zsb3cgcmVzdWx0cy4KCkFzIGEgZmlyc3Qgc3RlcCwgd2UgY29udmVydCB0aGUgb3V0cHV0IG9mIGVhY2ggdG9vbCB0byBhIGNvbnNvbGlkYXRlZCAic3RhbmRhcmQiIGZvcm1hdCwgYXMgZXhwZWN0ZWQgYnkgR2VuZVRvbmljIC0gdGhpcyBpcyB0aGUgZmlyc3Qgc3RlcCB0byBjb25zdHJ1Y3QgYSBgR2VuZVRvbmljTGlzdGAgb2JqZWN0LCBhcyBhIHNpbmdsZSBjb250YWluZXIgdG8gcGVyZm9ybSBhbGwgb3BlcmF0aW9ucyBvbiBhZnRlcndhcmRzLCBiZSBpdCBpbiB0aGUgYXBwIG9yIG9mZmxpbmUgYnkgdXNpbmcgaXRzIGZ1bmN0aW9uYWxpdHkgaW4gc2NyaXB0cy9ub3RlYm9va3MuCgpgYGB7ciBzaGFrZS1mdW5jcmVzfQpyZXNfZW5yaWNoX3RvcEdPIDwtIHNoYWtlX3RvcEdPdGFibGVSZXN1bHQodG9wZ29fREVfbWFjcm9waGFnZV9JRk5nX3ZzX25haXZlKQpyZXNfZW5yaWNoX2NsdXBybyA8LSBzaGFrZV9lbnJpY2hSZXN1bHQoY2x1cHJvX0RFX21hY3JvcGhhZ2VfSUZOZ192c19uYWl2ZSkKCmd0bF9tYWNyb3BoYWdlIDwtIEdlbmVUb25pY0xpc3QoCiAgICBkZHMgPSBkZHMsCiAgICByZXNfZGUgPSByZXMsCiAgICByZXNfZW5yaWNoID0gcmVzX2VucmljaF9jbHVwcm8sCiAgICBhbm5vdGF0aW9uX29iaiA9IGRhdGEuZnJhbWUoCiAgICAgICAgZ2VuZV9pZCA9IHJvd0RhdGEoZGRzKSRnZW5lX2lkLAogICAgICAgIGdlbmVfbmFtZSA9IHJvd0RhdGEoZGRzKSRTWU1CT0wKICAgICkKKQoKZGVzY3JpYmVfZ3RsKGd0bF9tYWNyb3BoYWdlKQoKIyMgd2UgY2FuIHN0b3JlIHRoaXMgb2JqZWN0IGFzIHNlcmlhbGl6ZWQgZmlsZSB0byBsb2FkL3NoYXJlLy4uLgpzYXZlUkRTKGd0bF9tYWNyb3BoYWdlLCAiZ3RsX21hY3JvcGhhZ2UuUkRTIikKYGBgCgpBZnRlciB0aGF0LCB3ZSB3b3VsZCBzaW1wbHkgaGF2ZSB0byBjYWxsIHRoZSBgR2VuZVRvbmljKClgIGZ1bmN0aW9uIHNwZWNpZnlpbmcgdGhlIGBndGxgIHBhcmFtZXRlciAtIHRoaXMgY2FuIGFsc28gYmUgcGFzc2VkIGF0IHJ1bnRpbWUKCmBgYHtyIGxhdW5jaGd0LCBldmFsPUZBTFNFfQojIyBhbmQgdGhhdCBpcyBpdCEKR2VuZVRvbmljKGd0bCA9IGd0bF9tYWNyb3BoYWdlKQoKIyMgb3IgaWYgZXhwZWN0aW5nIHRvIHVwbG9hZCBhdCBydW50aW1lLi4uIChlLmcuIHVzZWQgYXMgYSBzZXJ2ZXItbGlrZSBhcHApCkdlbmVUb25pYygpCmBgYAoKCiMjIyBRdWl6IHstfQoKKiBHZW5lcyBvciBnZW5lc2V0OiBXaGF0IHNob3VsZCBJIHVzZSBhcyBhIGtleSB0byBpbnRlcnByZXRhdGlvbj8KKiBXaGF0IGlzIHRoZSBtYWluIGFkdmFudGFnZSBvZiB1c2luZyBzdGFuZGFyZGl6ZWQgb2JqZWN0cyB2cyBhIGJ1bmNoIG9mIHRhYmxlcz8KKiAiT2ssIEkgd291bGQgc3RpbGwgbGlrZSB5b3VyIEJpb2luZm9ybWF0aWNzIGdyb3VwIG9uIGJvYXJkIGZvciBteSBwcm9qZWN0LiBXaGF0IHNob3VsZCBJIGV4cGVjdCB0byBoYXBwZW4/IgoKIyBCb251czogRGlmZmVyZW50aWFsIHRyYW5zY3JpcHQgZXhwcmVzc2lvbiB3aXRoIHN3aXNoCgpOZXh0LCB3ZSBwZXJmb3JtIGEgZGlmZmVyZW50aWFsIHRyYW5zY3JpcHQgZXhwcmVzc2lvbiBhbmFseXNpcyB3aXRoIApgciBCaW9jcGtnKCJzd2lzaCIpYC4gTm90ZSB0aGF0LCBhcyBvcHBvc2VkIHRvIHRoZSB3b3JrZmxvdyBhYm92ZSwgd2Ugd2lsbCAKbWFrZSB1c2Ugb2YgdGhlIHRyYW5zY3JpcHQtbGV2ZWwgYWJ1bmRhbmNlIGVzdGltYXRlcy4gSW4gYWRkaXRpb24sIHdlIG5lZWQgdGhlIAppbmZlcmVudGlhbCByZXBsaWNhdGVzLiBTaW5jZSB3ZSBpZ25vcmVkIHRoZXNlIHdoZW4gaW1wb3J0aW5nIHRoZSBkYXRhIGFib3ZlLCAKd2Ugd2lsbCByZS1pbXBvcnQgaXQgaGVyZS4gCgpgYGB7ciBpbXBvcnRzd2lzaCwgZXZhbD1ldmFsX2R0ZX0KaGVhZChjb2xkYXRhKQpzdCA8LSB0eGltZXRhKGNvbGRhdGEgPSBjb2xkYXRhLCB0eXBlID0gInNhbG1vbiIsIGRyb3BJbmZSZXBzID0gRkFMU0UpCmBgYAoKV2UgY2FuIGNoZWNrIHRoYXQgdGhlIGluZmVyZW50aWFsIHJlcGxpY2F0ZXMgd2VyZSBpbXBvcnRlZCBhcyB3ZWxsOiAKCmBgYHtyIGNoZWNraW5mcmVwcywgZXZhbD1ldmFsX2R0ZX0KYXNzYXlOYW1lcyhzdCkKYGBgCgpTaW5jZSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBjb21wYXJpbmcgdGhlIGBuYWl2ZWAgYW5kIGBJRk5nYCBncm91cHMsIHdlIApzdWJzZXQgb3VyIG9iamVjdCB0byB0aGVzZSBncm91cHMuCgpgYGB7ciBzdWJzZXRzdCwgZXZhbD1ldmFsX2R0ZX0Kc3QgPC0gc3RbLCBzdCRjb25kaXRpb25fbmFtZSAlaW4lIGMoIm5haXZlIiwgIklGTmciKV0Kc3QkY29uZGl0aW9uX25hbWUgPC0gZmFjdG9yKHN0JGNvbmRpdGlvbl9uYW1lLCBjKCJuYWl2ZSIsICJJRk5nIikpCmBgYAoKTmV4dCwgd2UgcnVuIHRoZSBEVEUgYW5hbHlzaXMgd2l0aCAqc3dpc2gqLiBGaXJzdCB3ZSdsbCBzY2FsZSB0aGUgaW5mZXJlbnRpYWwgCnJlcGxpY2F0ZXMsIGZvbGxvd2VkIGJ5IGxhYmVsaW5nIHRoZSByb3dzIHdpdGggc3VmZmljaWVudCBjb3VudHMgZm9yIHJ1bm5pbmcgCmRpZmZlcmVudGlhbCBleHByZXNzaW9uLCBhbmQgdGhlbiBjYWxjdWxhdGluZyB0aGUgc3RhdGlzdGljcy4gCgpgYGB7ciBzd2lzaGR0ZSwgbWVzc2FnZSA9IEZBTFNFLCBldmFsPWV2YWxfZHRlfQpsaWJyYXJ5KGZpc2hwb25kKQpzdCA8LSBzY2FsZUluZlJlcHMoc3QsIGxlbmd0aENvcnJlY3QgPSBUUlVFKQpzdCA8LSBsYWJlbEtlZXAoc3QpCnN0IDwtIHN0W21jb2xzKHN0KSRrZWVwLCBdCnNldC5zZWVkKDEpCnN0IDwtIHN3aXNoKHN0LCB4ID0gImNvbmRpdGlvbl9uYW1lIiwgcGFpciA9ICJsaW5lX2lkIiwgbnBlcm1zID0gMTAwKQpgYGAKClRoZSByZXN1bHRzIGFyZSBzdG9yZWQgaW4gYG1jb2xzKHN0KWAuIAoKYGBge3IgY2hlY2tzdCwgZXZhbD1ldmFsX2R0ZX0KaGVhZChtY29scyhzdCkpCnRhYmxlKG1jb2xzKHN0KSRxdmFsdWUgPCAwLjA1KQojIyBNb3N0IHNpZ25pZmljYW50IHRyYW5zY3JpcHRzCnRvcGhpdHMgPC0gbWNvbHMoc3QpW29yZGVyKG1jb2xzKHN0KSRxdmFsdWUsIC1hYnMobWNvbHMoc3QpJGxvZzJGQykpLCBdCmhlYWQodG9waGl0cykKaGlzdChtY29scyhzdCkkcHZhbHVlLCBjb2wgPSAiZ3JleSIpCmBgYAoKV2UgY2FuIHBsb3QgdGhlIHJlc3VsdHMgZm9yIHNvbWUgb2YgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgdHJhbnNjcmlwdHMuCgpgYGB7ciBwbG90ZHRlLCBldmFsPWV2YWxfZHRlfQpwbG90SW5mUmVwcyhzdCwgaWR4ID0gcm93bmFtZXModG9waGl0cylbMV0sCiAgICAgICAgICAgIHggPSAiY29uZGl0aW9uX25hbWUiLCBjb3YgPSAibGluZV9pZCIpCnBsb3RJbmZSZXBzKHN0LCBpZHggPSByb3duYW1lcyh0b3BoaXRzKVs0M10sCiAgICAgICAgICAgIHggPSAiY29uZGl0aW9uX25hbWUiLCBjb3YgPSAibGluZV9pZCIpCnBsb3RJbmZSZXBzKHN0LCBpZHggPSByb3duYW1lcyh0b3BoaXRzKVs2MF0sCiAgICAgICAgICAgIHggPSAiY29uZGl0aW9uX25hbWUiLCBjb3YgPSAibGluZV9pZCIpCnBsb3RNQVN3aXNoKHN0LCBhbHBoYSA9IDAuMDUpCmBgYAoKV2UgY2FuIGFsc28gdXNlIHRoZSBwbG90dGluZyBmdW5jdGlvbnMgb2YgKnN3aXNoKiB0byBwbG90IHRoZSBpbmZlcmVudGlhbCAKcmVwbGljYXRlcyBmb3IgdGhlIHRvcC1yYW5rZWQgZ2VuZXMgaW4gdGhlIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gCmFuYWx5c2lzLiAKCmBgYHtyIHBsb3Rzd2lzaGRnZSwgZXZhbD1ldmFsX2R0ZX0KIyMgU3VtbWFyaXplIG9uIHRoZSBnZW5lIGxldmVsCnNnX2luZiA8LSBzdW1tYXJpemVUb0dlbmUoc3QpCnNnX2luZiA8LSBzZ19pbmZbLCBzZ19pbmYkY29uZGl0aW9uX25hbWUgJWluJSBjKCJuYWl2ZSIsICJJRk5nIildCnNnX2luZiRjb25kaXRpb25fbmFtZSA8LSBmYWN0b3Ioc2dfaW5mJGNvbmRpdGlvbl9uYW1lLCBjKCJuYWl2ZSIsICJJRk5nIikpCnBsb3RJbmZSZXBzKHNnX2luZiwgaWR4ID0gcm93bmFtZXMocmVzT3JkZXJlZClbMV0sCiAgICAgICAgICAgIHggPSAiY29uZGl0aW9uX25hbWUiLCBjb3YgPSAibGluZV9pZCIpCmBgYAoKIyBCb251czogRGlmZmVyZW50aWFsIHRyYW5zY3JpcHQgdXNhZ2UKCkZpbmFsbHksIHdlIHNob3cgaG93IHRvIHJ1biBkaWZmZXJlbnRpYWwgdHJhbnNjcmlwdCB1c2FnZSBhbmFseXNpcyB3aXRoIAoqc3dpc2gqIGFuZCBgciBCaW9jcGtnKCJERVhTZXEiKWAuIApXaXRoICpzd2lzaCosIHdlIGNhbiBidWlsZCB1cG9uIHRoZSBkYXRhIGltcG9ydGVkIGVhcmxpZXIgdG8gY2FsY3VsYXRlIAppc29mb3JtIHByb3BvcnRpb25zIGFuZCBwZXJmb3JtIGEgcGVybXV0YXRpb24gdGVzdCBiYXNlZCBvbiB0aGVzZS4gCgpgYGB7ciBzd2lzaGR0dSwgZXZhbD1ldmFsX2R0dX0KaXNvIDwtIGlzb2Zvcm1Qcm9wb3J0aW9ucyhzdCkKaXNvIDwtIHN3aXNoKGlzbywgeCA9ICJjb25kaXRpb25fbmFtZSIsIHBhaXIgPSAibGluZV9pZCIsIAogICAgICAgICAgICAgbnBlcm1zID0gMTAwKQpgYGAKCkZvciAqREVYU2VxKiwgd2Ugd2lsbCBhZ2FpbiByZS1pbXBvcnQgdGhlIGRhdGEsIHNpbmNlIHdlIHdvdWxkIGxpa2UgdGhlIAp0cmFuc2NyaXB0IGNvdW50cyB0byByZXByZXNlbnQgc28gY2FsbGVkICdzY2FsZWQgVFBNJyB2YWx1ZXMgKHNpbWlsYXJseSB0byAKd2hhdCAqc3dpc2gqIHdpbGwgZG8gaW50ZXJuYWxseSB3aGVuIHNjYWxpbmcgdGhlIGluZmVyZW50aWFsIHJlcGxpY2F0ZXMsIAp0byBhdm9pZCBkaWZmZXJlbmNlcyBpbiB0cmFuc2NyaXB0IGxlbmd0aCBiZWluZyBpbnRlcnByZXRlZCBhcyBkaWZmZXJlbmNlcyAKaW4gcmVsYXRpdmUgYWJ1bmRhbmNlIGJldHdlZW4gZ3JvdXBzKS4gKnR4aW1ldGEqIGNhbiBkbyB0aGlzIGZvciB1cywgCmVmZmVjdGl2ZWx5IHBvcHVsYXRpbmcgdGhlICdjb3VudHMnIGFzc2F5IHdpdGggVFBNcywgc2NhbGVkIHVwIHRvIHRoZSAKb2JzZXJ2ZWQgbGlicmFyeSBzaXplIHRvIGJlIGNvbXBhcmFibGUgaW4gc2l6ZSB0byB0aGUgYWN0dWFsIGNvdW50cy4gRm9yIAoqREVYU2VxKiwgd2UgZnVydGhlciBzdWJzZXQgdGhlIHRyYW5zY3JpcHRzIHRvIHRob3NlIG9uIGNocm9tb3NvbWUgMTgsIGZvciAKY29tcHV0YXRpb25hbCB0aW1lIHJlYXNvbnMuIAoKYGBge3IgZGV4c2VxLCBldmFsPWV2YWxfZHR1fQpoZWFkKGNvbGRhdGEpCnN0IDwtIHR4aW1ldGEoY29sZGF0YSA9IGNvbGRhdGEsIHR5cGUgPSAic2FsbW9uIiwgCiAgICAgICAgICAgICAgY291bnRzRnJvbUFidW5kYW5jZSA9ICJzY2FsZWRUUE0iKQpzdCA8LSBzdFssIHN0JGNvbmRpdGlvbl9uYW1lICVpbiUgYygibmFpdmUiLCAiSUZOZyIpXQpzdCRjb25kaXRpb25fbmFtZSA8LSBmYWN0b3Ioc3QkY29uZGl0aW9uX25hbWUsIGMoIm5haXZlIiwgIklGTmciKSkKc3Qkc2FtcGxlX2lkIDwtIGNvbG5hbWVzKHN0KQpzdCA8LSBzdFtzZXFuYW1lcyhyb3dSYW5nZXMoc3QpKSA9PSAiY2hyMTgiLCBdCgpsaWJyYXJ5KERFWFNlcSkKZHhkIDwtIERFWFNlcURhdGFTZXQoY291bnREYXRhID0gcm91bmQoYXNzYXkoc3QsICJjb3VudHMiKSksCiAgICAgICAgICAgICAgICAgICAgIHNhbXBsZURhdGEgPSBhcy5kYXRhLmZyYW1lKGNvbERhdGEoc3QpKSwKICAgICAgICAgICAgICAgICAgICAgZGVzaWduID0gfnNhbXBsZSArIGV4b24gKyBjb25kaXRpb25fbmFtZTpleG9uLAogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlSUQgPSByb3dEYXRhKHN0KSR0eF9uYW1lLAogICAgICAgICAgICAgICAgICAgICBncm91cElEID0gYXMuY2hhcmFjdGVyKHJvd0RhdGEoc3QpJGdlbmVfaWQpKQpkeGQgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkeGQpCmR4ZCA8LSBlc3RpbWF0ZURpc3BlcnNpb25zKGR4ZCwgcXVpZXQgPSBUUlVFKQpkeGQgPC0gdGVzdEZvckRFVShkeGQsIHJlZHVjZWRNb2RlbCA9IH5zYW1wbGUgKyBleG9uKQpkeHIgPC0gREVYU2VxUmVzdWx0cyhkeGQsIGluZGVwZW5kZW50RmlsdGVyaW5nID0gRkFMU0UpCnF2YWwgPC0gcGVyR2VuZVFWYWx1ZShkeHIpCmR4ci5nIDwtIGRhdGEuZnJhbWUoZ2VuZSA9IG5hbWVzKHF2YWwpLCBxdmFsKQpoZWFkKGR4cikKYGBgCgpGaW5hbGx5LCB3ZSBjb21wYXJlIHRoZSBwLXZhbHVlIHJhbmtzIGZvciB0aGUgZ2VuZXMgc2hhcmVkIGJ5IHRoZSB0d28gYW5hbHlzZXMuIAoKYGBge3IgcGxvdHN3aXNoZGV4c2VxLCBldmFsPWV2YWxfZHR1fQpzaGFyZWQgPC0gaW50ZXJzZWN0KGR4ciRmZWF0dXJlSUQsIHJvd25hbWVzKG1jb2xzKGlzbykpKQpwbG90KHJhbmsoZHhyW21hdGNoKHNoYXJlZCwgZHhyJGZlYXR1cmVJRCksICJwdmFsdWUiXSksCiAgICAgcmFuayhtY29scyhpc28pW3NoYXJlZCwgInB2YWx1ZSJdKSwgY2V4ID0gMC4xLAogICAgIHhsYWIgPSAiREVYU2VxIiwgeWxhYiA9ICJzd2lzaCIpCmBgYAoKIyBTZXNzaW9uIGluZm9ybWF0aW9uIHstfQoKSXQgaXMgZ29vZCBwcmFjdGljZSB0byBhbHdheXMgaW5jbHVkZSBhIGxpc3Qgb2YgdGhlIHNvZnR3YXJlIHZlcnNpb25zIHRoYXQgd2VyZSB1c2VkIHRvIHBlcmZvcm0gYSBnaXZlbiBhbmFseXNpcywgZm9yIHJlcHJvZHVjaWJpbGl0eSBhbmQgdHJvdWJsZS1zaG9vdGluZyBwdXJwb3Nlcy4gCk9uZSB3YXkgb2YgYWNoaWV2aW5nIHRoaXMgaXMgdmlhIHRoZSBgc2Vzc2lvbkluZm8oKWAgZnVuY3Rpb24uIAoKYGBge3Igc2Vzc2lvbmluZm99CnNlc3Npb25JbmZvKCkKYGBgCgojIFJlZmVyZW5jZXMgey19Cgo=